/********************************************************************************
* CruiseControl, a Continuous Integration Toolkit
* Copyright (c) 2001, ThoughtWorks, Inc.
* 200 E. Randolph, 25th Floor
* Chicago, IL 60601 USA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* + Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
********************************************************************************/
package net.sourceforge.cruisecontrol;
import net.sourceforge.cruisecontrol.util.DateUtil;
import org.apache.log4j.Logger;
import org.jdom.CDATA;
import org.jdom.Element;
import org.jdom.output.XMLOutputter;
import java.io.Serializable;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Iterator;
/**
* data structure for holding data about a single modification
* to a source control tool.
* <pre>
* {@code
* <modification type="" date="" user="" email="">
* <comment></comment>
* <file >
* </modification>
* }
* </pre>
*
* @author <a href="mailto:alden@thoughtworks.com">alden almagro</a>
*/
public class Modification implements Comparable<Modification>, Serializable {
private static final long serialVersionUID = 6102576575583133520L;
private static final Logger LOG = Logger.getLogger(Modification.class);
private static final String TAGNAME_MODIFICATION = "modification";
private static final String TAGNAME_TYPE = "type";
private static final String TAGNAME_FILE = "file";
private static final String TAGNAME_FILENAME = "filename";
private static final String TAGNAME_FOLDERNAME = "project";
private static final String TAGNAME_DATE = "date";
private static final String TAGNAME_USER = "user";
private static final String TAGNAME_COMMENT = "comment";
private static final String TAGNAME_EMAIL = "email";
private static final String TAGNAME_REVISION = "revision";
private static final String TAGNAME_ACTION = "action";
public static class ModifiedFile {
public String fileName;
public String revision;
public String folderName;
public String action = "unknown";
public ModifiedFile(final String fileName, final String revision, final String folderName,
final String action) {
this.fileName = fileName;
this.revision = revision;
this.folderName = folderName;
this.action = action;
}
public ModifiedFile(final Element modification) {
fileName = modification.getChildText(TAGNAME_FILENAME);
folderName = modification.getChildText(TAGNAME_FOLDERNAME);
revision = modification.getChildText(TAGNAME_REVISION);
action = modification.getAttributeValue(TAGNAME_ACTION);
}
public Element toElement() {
final Element element = new Element(TAGNAME_FILE);
if (revision != null && revision.trim().length() > 0) {
final Element revisionElement = new Element(TAGNAME_REVISION);
revisionElement.addContent(revision);
element.addContent(revisionElement);
}
if (action != null && action.trim().length() > 0) {
element.setAttribute(TAGNAME_ACTION, action);
}
final Element fileElement = new Element(TAGNAME_FILENAME);
fileElement.addContent(fileName);
element.addContent(fileElement);
if (folderName != null && folderName.trim().length() > 0) {
final Element folderElement = new Element(TAGNAME_FOLDERNAME);
folderElement.addContent(folderName);
element.addContent(folderElement);
}
return element;
}
public boolean equals(final Object o) {
if (!(o instanceof ModifiedFile)) {
return false;
}
final ModifiedFile mod = (ModifiedFile) o;
boolean folderNamesAreEqual = (folderName != null)
? folderName.equals(mod.folderName)
: (mod.folderName == null);
boolean revisionsAreEqual = (revision != null)
? revision.equals(mod.revision)
: (mod.revision == null);
return (action.equals(mod.action)
&& fileName.equals(mod.fileName)
&& folderNamesAreEqual
&& revisionsAreEqual);
}
public int hashCode() {
int code = 1;
if (fileName != null) {
code += fileName.hashCode() * 2;
}
if (revision != null) {
code += revision.hashCode() * 3;
}
if (folderName != null) {
code += folderName.hashCode() * 5;
}
if (action != null) {
code += action.hashCode() * 7;
}
return code;
}
public String getFileName() {
return fileName;
}
public String getRevision() {
return revision;
}
public String getFolderName() {
return folderName;
}
public String getAction() {
return action;
}
}
public String type = "";
public String userName = "";
public String comment = "";
public String emailAddress;
public String revision;
public Date modifiedTime;
public List<ModifiedFile> files = new ArrayList<ModifiedFile>();
public Modification() {
this("unknown");
}
public Modification(String type) {
this.type = type;
}
public Modification(final String type, final String user, final String comment, final String email,
final Date datetime, final String revision, final List<ModifiedFile> files) {
this.type = type;
this.userName = user;
this.comment = comment;
this.emailAddress = email;
this.modifiedTime = datetime;
this.revision = revision;
this.files = files;
}
public final ModifiedFile createModifiedFile(String filename, String folder) {
ModifiedFile file = new ModifiedFile(filename, "", folder, "unknown");
files.add(file);
return file;
}
public Element toElement() {
final Element modificationElement = new Element(TAGNAME_MODIFICATION);
modificationElement.setAttribute(TAGNAME_TYPE, type);
for (final ModifiedFile file : files) {
modificationElement.addContent(file.toElement());
}
final Element dateElement = new Element(TAGNAME_DATE);
dateElement.addContent(DateUtil.formatIso8601(modifiedTime));
final Element userElement = new Element(TAGNAME_USER);
userElement.addContent(userName);
final Element commentElement = new Element(TAGNAME_COMMENT);
CDATA cd;
try {
cd = new CDATA(comment);
} catch (org.jdom.IllegalDataException e) {
LOG.error(e);
cd = new CDATA("Unable to parse comment. It contains illegal data.");
}
commentElement.addContent(cd);
modificationElement.addContent(dateElement);
modificationElement.addContent(userElement);
modificationElement.addContent(commentElement);
if (revision != null && revision.trim().length() > 0) {
final Element revisionElement = new Element(TAGNAME_REVISION);
revisionElement.addContent(revision);
modificationElement.addContent(revisionElement);
}
// not all sourcecontrols guarantee a non-null email address
if (emailAddress != null) {
final Element emailAddressElement = new Element(TAGNAME_EMAIL);
emailAddressElement.addContent(emailAddress);
modificationElement.addContent(emailAddressElement);
}
return modificationElement;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("Type: ").append(type).append('\n');
sb.append("Last Modified: ").append(DateUtil.formatIso8601(modifiedTime)).append('\n');
sb.append("Revision: ").append(revision).append('\n');
sb.append("UserName: ").append(userName).append('\n');
sb.append("EmailAddress: ").append(emailAddress).append('\n');
sb.append("Comment: ").append(comment).append('\n');
return sb.toString();
}
public void log() {
if (LOG.isDebugEnabled()) {
LOG.debug("Type: " + type);
LOG.debug("Last Modified: " + DateUtil.formatIso8601(modifiedTime));
LOG.debug("UserName: " + userName);
LOG.debug("EmailAddress: " + emailAddress);
LOG.debug("Comment: " + comment);
LOG.debug("");
}
}
/**
* Convenience method for getting the filename of the first file.
* @return the filename of the first file
*/
public String getFileName() {
if (files.isEmpty()) {
return null;
} else {
return files.get(0).fileName;
}
}
/**
* Convenience method for getting the foldername of the first file.
* @return the foldername of the first file
*/
public String getFolderName() {
if (files.isEmpty()) {
return null;
} else {
return files.get(0).folderName;
}
}
/**
* Returns the list of modified files for this modification set.
*
* @return list of {@link ModifiedFile} objects. If there are no files, this returns an empty list
* (<code>null</code> is never returned).
*/
public List<ModifiedFile> getModifiedFiles() {
return Collections.unmodifiableList(files);
}
public int compareTo(final Modification modification) {
return modifiedTime.compareTo(modification.modifiedTime);
}
public boolean equals(Object o) {
if (!(o instanceof Modification)) {
return false;
}
Modification mod = (Modification) o;
boolean emailsAreEqual = (emailAddress != null)
? emailAddress.equals(mod.emailAddress)
: (mod.emailAddress == null);
boolean revisionsAreEqual = (revision != null)
? revision.equals(mod.revision)
: (mod.revision == null);
boolean filesAreEqual = files.size() == mod.files.size();
for (int i = 0; filesAreEqual && i < files.size(); i++) {
filesAreEqual = mod.files.get(i).equals(files.get(i));
}
return (
type.equals(mod.type)
&& modifiedTime.equals(mod.modifiedTime)
&& userName.equals(mod.userName)
&& revisionsAreEqual
&& emailsAreEqual
&& comment.equals(mod.comment));
}
public int hashCode() {
int code = 1;
if (type != null) {
code += type.hashCode() * 2;
}
if (modifiedTime != null) {
code += modifiedTime.getTime();
}
if (userName != null) {
code += userName.hashCode() * 5;
}
if (emailAddress != null) {
code += emailAddress.hashCode() * 7;
}
if (comment != null) {
code += comment.hashCode() * 11;
}
if (revision != null) {
code += revision.hashCode() * 13;
}
if (files != null) {
code += fileHashComponent();
}
return code;
}
private int fileHashComponent() {
int code = 1;
for (final ModifiedFile file : files) {
code += file.hashCode();
}
return code;
}
// TODO: remove this after we have a ModifationSet returning Modificaitons rather than an Element.
public void fromElement(final Element modification) {
type = modification.getAttributeValue(TAGNAME_TYPE);
try {
final String s = modification.getChildText(TAGNAME_DATE);
if (s == null) {
XMLOutputter outputter = new XMLOutputter();
LOG.info("XML: " + outputter.outputString(modification));
}
modifiedTime = DateUtil.parseIso8601(s);
} catch (ParseException e) {
LOG.warn("exception parsing date from Modification Element", e);
//maybe we should do something different
modifiedTime = new Date();
}
revision = modification.getChildText(TAGNAME_REVISION);
userName = modification.getChildText(TAGNAME_USER);
comment = modification.getChildText(TAGNAME_COMMENT);
emailAddress = modification.getChildText(TAGNAME_EMAIL);
files.clear();
final List modfiles = modification.getChildren(TAGNAME_FILE);
if (modfiles != null && modfiles.size() > 0) {
final Iterator it = modfiles.iterator();
while (it.hasNext()) {
Element modfileElement = (Element) it.next();
ModifiedFile modfile = new ModifiedFile(modfileElement);
files.add(modfile);
}
}
}
/**
* Concatenates the folderName and fileName of the Modification into a
* <code>String</code>. If the folderName is null then it is not included.
* All backward slashes ("\") are converted to forward slashes
* ("/").
*
* @return A <code>String</code> containing the full path
* of the modification
*/
public String getFullPath() {
StringBuffer result = new StringBuffer();
String folderName = getFolderName();
if (folderName != null) {
result.append(folderName).append("/");
}
result.append(getFileName());
return result.toString().replace('\\', '/');
}
public String getType() {
return type;
}
public Date getModifiedTime() {
return modifiedTime;
}
public String getUserName() {
return userName;
}
public String getEmailAddress() {
return emailAddress;
}
public String getRevision() {
return revision;
}
public String getComment() {
return comment;
}
}