001: /********************************************************************************
002: * CruiseControl, a Continuous Integration Toolkit
003: * Copyright (c) 2001, ThoughtWorks, Inc.
004: * 200 E. Randolph, 25th Floor
005: * Chicago, IL 60601 USA
006: * All rights reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: *
012: * + Redistributions of source code must retain the above copyright
013: * notice, this list of conditions and the following disclaimer.
014: *
015: * + Redistributions in binary form must reproduce the above
016: * copyright notice, this list of conditions and the following
017: * disclaimer in the documentation and/or other materials provided
018: * with the distribution.
019: *
020: * + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
021: * names of its contributors may be used to endorse or promote
022: * products derived from this software without specific prior
023: * written permission.
024: *
025: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
026: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
027: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
028: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
029: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
030: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
031: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
032: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
033: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
034: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
035: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
036: ********************************************************************************/package net.sourceforge.cruisecontrol;
037:
038: import org.apache.log4j.Logger;
039: import org.jdom.CDATA;
040: import org.jdom.Element;
041: import org.jdom.output.XMLOutputter;
042:
043: import java.io.Serializable;
044: import java.text.DateFormat;
045: import java.text.ParseException;
046: import java.text.SimpleDateFormat;
047: import java.util.ArrayList;
048: import java.util.Collections;
049: import java.util.Date;
050: import java.util.Iterator;
051: import java.util.List;
052:
053: /**
054: * data structure for holding data about a single modification
055: * to a source control tool.
056: *
057: * <modification type="" date="" user="" email="">
058: * <comment></comment>
059: * <file >
060: * </modification>
061: *
062: * @author <a href="mailto:alden@thoughtworks.com">alden almagro</a>
063: */
064: public class Modification implements Comparable, Serializable {
065:
066: private static final long serialVersionUID = 6102576575583133520L;
067:
068: private static final Logger LOG = Logger
069: .getLogger(Modification.class);
070:
071: private static final String TAGNAME_MODIFICATION = "modification";
072: private static final String TAGNAME_TYPE = "type";
073: private static final String TAGNAME_FILE = "file";
074: private static final String TAGNAME_FILENAME = "filename";
075: private static final String TAGNAME_FOLDERNAME = "project";
076: private static final String TAGNAME_DATE = "date";
077: private static final String TAGNAME_USER = "user";
078: private static final String TAGNAME_COMMENT = "comment";
079: private static final String TAGNAME_EMAIL = "email";
080: private static final String TAGNAME_REVISION = "revision";
081: private static final String TAGNAME_ACTION = "action";
082:
083: public static class ModifiedFile {
084:
085: public String fileName;
086: public String revision;
087: public String folderName;
088: public String action = "unknown";
089:
090: public ModifiedFile(String fileName, String revision,
091: String folderName, String action) {
092: this .fileName = fileName;
093: this .revision = revision;
094: this .folderName = folderName;
095: this .action = action;
096: }
097:
098: public ModifiedFile(Element modification, DateFormat formatter) {
099: fileName = modification.getChildText(TAGNAME_FILENAME);
100: folderName = modification.getChildText(TAGNAME_FOLDERNAME);
101: revision = modification.getChildText(TAGNAME_REVISION);
102: action = modification.getAttributeValue(TAGNAME_ACTION);
103: }
104:
105: public Element toElement(DateFormat formatter) {
106:
107: Element element = new Element(TAGNAME_FILE);
108:
109: if (revision != null && revision.trim().length() > 0) {
110: Element revisionElement = new Element(TAGNAME_REVISION);
111: revisionElement.addContent(revision);
112: element.addContent(revisionElement);
113: }
114:
115: if (action != null && action.trim().length() > 0) {
116: element.setAttribute(TAGNAME_ACTION, action);
117: }
118:
119: Element fileElement = new Element(TAGNAME_FILENAME);
120: fileElement.addContent(fileName);
121: element.addContent(fileElement);
122:
123: if (folderName != null && folderName.trim().length() > 0) {
124: Element folderElement = new Element(TAGNAME_FOLDERNAME);
125: folderElement.addContent(folderName);
126: element.addContent(folderElement);
127: }
128:
129: return element;
130:
131: }
132:
133: public boolean equals(Object o) {
134: if (!(o instanceof ModifiedFile)) {
135: return false;
136: }
137:
138: ModifiedFile mod = (ModifiedFile) o;
139:
140: boolean folderNamesAreEqual = (folderName != null) ? folderName
141: .equals(mod.folderName)
142: : (mod.folderName == null);
143:
144: boolean revisionsAreEqual = (revision != null) ? revision
145: .equals(mod.revision) : (mod.revision == null);
146:
147: return (action.equals(mod.action)
148: && fileName.equals(mod.fileName)
149: && folderNamesAreEqual && revisionsAreEqual);
150: }
151:
152: public int hashCode() {
153: int code = 1;
154: if (fileName != null) {
155: code += fileName.hashCode() * 2;
156: }
157: if (revision != null) {
158: code += revision.hashCode() * 3;
159: }
160: if (folderName != null) {
161: code += folderName.hashCode() * 5;
162: }
163: if (action != null) {
164: code += action.hashCode() * 7;
165: }
166: return code;
167: }
168:
169: public String getFileName() {
170: return fileName;
171: }
172:
173: public String getRevision() {
174: return revision;
175: }
176:
177: public String getFolderName() {
178: return folderName;
179: }
180:
181: public String getAction() {
182: return action;
183: }
184: }
185:
186: public String type = "";
187: public String userName = "";
188: public String comment = "";
189: public String emailAddress;
190: public String revision;
191: public Date modifiedTime;
192: public List files = new ArrayList();
193:
194: public Modification() {
195: this ("unknown");
196: }
197:
198: public Modification(String type) {
199: this .type = type;
200: }
201:
202: public Modification(String type, String user, String comment,
203: String email, Date datetime, String revision, List files) {
204: this .type = type;
205: this .userName = user;
206: this .comment = comment;
207: this .emailAddress = email;
208: this .modifiedTime = datetime;
209: this .revision = revision;
210: this .files = files;
211: }
212:
213: public final ModifiedFile createModifiedFile(String filename,
214: String folder) {
215: ModifiedFile file = new ModifiedFile(filename, "", folder,
216: "unknown");
217: files.add(file);
218: return file;
219: }
220:
221: public Element toElement(DateFormat formatter) {
222: Element modificationElement = new Element(TAGNAME_MODIFICATION);
223: modificationElement.setAttribute(TAGNAME_TYPE, type);
224:
225: for (Iterator i = files.iterator(); i.hasNext();) {
226: modificationElement.addContent(((ModifiedFile) i.next())
227: .toElement(formatter));
228: }
229:
230: Element dateElement = new Element(TAGNAME_DATE);
231: dateElement.addContent(formatter.format(modifiedTime));
232: Element userElement = new Element(TAGNAME_USER);
233: userElement.addContent(userName);
234: Element commentElement = new Element(TAGNAME_COMMENT);
235:
236: CDATA cd;
237: try {
238: cd = new CDATA(comment);
239: } catch (org.jdom.IllegalDataException e) {
240: LOG.error(e);
241: cd = new CDATA(
242: "Unable to parse comment. It contains illegal data.");
243: }
244: commentElement.addContent(cd);
245:
246: modificationElement.addContent(dateElement);
247: modificationElement.addContent(userElement);
248: modificationElement.addContent(commentElement);
249:
250: if (revision != null && revision.trim().length() > 0) {
251: Element revisionElement = new Element(TAGNAME_REVISION);
252: revisionElement.addContent(revision);
253: modificationElement.addContent(revisionElement);
254: }
255:
256: // not all sourcecontrols guarantee a non-null email address
257: if (emailAddress != null) {
258: Element emailAddressElement = new Element(TAGNAME_EMAIL);
259: emailAddressElement.addContent(emailAddress);
260: modificationElement.addContent(emailAddressElement);
261: }
262:
263: return modificationElement;
264: }
265:
266: public String toXml(DateFormat formatter) {
267: return new XMLOutputter().outputString(toElement(formatter));
268: }
269:
270: public String toString() {
271: SimpleDateFormat formatter = new SimpleDateFormat(
272: "yyyy/MM/dd HH:mm:ss");
273: StringBuffer sb = new StringBuffer();
274: sb.append("Type: ").append(type).append('\n');
275: sb.append("Last Modified: ").append(
276: formatter.format(modifiedTime)).append('\n');
277: sb.append("Revision: ").append(revision).append('\n');
278: sb.append("UserName: ").append(userName).append('\n');
279: sb.append("EmailAddress: ").append(emailAddress).append('\n');
280: sb.append("Comment: ").append(comment).append('\n');
281: return sb.toString();
282: }
283:
284: public void log(DateFormat formatter) {
285: if (LOG.isDebugEnabled()) {
286: LOG.debug("Type: " + type);
287: LOG.debug("Last Modified: "
288: + formatter.format(modifiedTime));
289: LOG.debug("UserName: " + userName);
290: LOG.debug("EmailAddress: " + emailAddress);
291: LOG.debug("Comment: " + comment);
292: LOG.debug("");
293: }
294: }
295:
296: /**
297: * Convenience method for getting the filename of the first file
298: */
299: public String getFileName() {
300: if (files.isEmpty()) {
301: return null;
302: } else {
303: return ((ModifiedFile) files.get(0)).fileName;
304: }
305: }
306:
307: /**
308: * Convenience method for getting the foldername of the first file
309: */
310: public String getFolderName() {
311: if (files.isEmpty()) {
312: return null;
313: } else {
314: return ((ModifiedFile) files.get(0)).folderName;
315: }
316: }
317:
318: /**
319: * Returns the list of modified files for this modification set.
320: *
321: * @return list of {@link ModifiedFile} objects. If there are no files, this returns an empty list
322: * (<code>null</code> is never returned).
323: */
324: public List getModifiedFiles() {
325: return Collections.unmodifiableList(files);
326: }
327:
328: public int compareTo(Object o) {
329: Modification modification = (Modification) o;
330: return modifiedTime.compareTo(modification.modifiedTime);
331: }
332:
333: public boolean equals(Object o) {
334: if (!(o instanceof Modification)) {
335: return false;
336: }
337:
338: Modification mod = (Modification) o;
339:
340: boolean emailsAreEqual = (emailAddress != null) ? emailAddress
341: .equals(mod.emailAddress) : (mod.emailAddress == null);
342:
343: boolean revisionsAreEqual = (revision != null) ? revision
344: .equals(mod.revision) : (mod.revision == null);
345:
346: boolean filesAreEqual = files.size() == mod.files.size();
347: for (int i = 0; filesAreEqual && i < files.size(); i++) {
348: filesAreEqual = mod.files.get(i).equals(files.get(i));
349: }
350:
351: return (type.equals(mod.type)
352: && modifiedTime.equals(mod.modifiedTime)
353: && userName.equals(mod.userName) && revisionsAreEqual
354: && emailsAreEqual && comment.equals(mod.comment));
355: }
356:
357: public int hashCode() {
358: int code = 1;
359: if (type != null) {
360: code += type.hashCode() * 2;
361: }
362: if (modifiedTime != null) {
363: code += modifiedTime.getTime();
364: }
365: if (userName != null) {
366: code += userName.hashCode() * 5;
367: }
368: if (emailAddress != null) {
369: code += emailAddress.hashCode() * 7;
370: }
371: if (comment != null) {
372: code += comment.hashCode() * 11;
373: }
374: if (revision != null) {
375: code += revision.hashCode() * 13;
376: }
377: if (files != null) {
378: code += fileHashComponent();
379: }
380: return code;
381: }
382:
383: private int fileHashComponent() {
384: int code = 1;
385: for (int i = 0; i < files.size(); i++) {
386: Modification.ModifiedFile file = (ModifiedFile) files
387: .get(i);
388: code += file.hashCode();
389: }
390: return code;
391: }
392:
393: public void fromElement(Element modification, DateFormat formatter) {
394:
395: type = modification.getAttributeValue(TAGNAME_TYPE);
396: try {
397: String s = modification.getChildText(TAGNAME_DATE);
398: if (s == null) {
399: XMLOutputter outputter = new XMLOutputter();
400: LOG
401: .info("XML: "
402: + outputter.outputString(modification));
403: }
404:
405: modifiedTime = formatter.parse(s);
406: } catch (ParseException e) {
407: //maybe we should do something different
408: modifiedTime = new Date();
409: }
410:
411: revision = modification.getChildText(TAGNAME_REVISION);
412: userName = modification.getChildText(TAGNAME_USER);
413: comment = modification.getChildText(TAGNAME_COMMENT);
414: emailAddress = modification.getChildText(TAGNAME_EMAIL);
415:
416: files.clear();
417: List modfiles = modification.getChildren(TAGNAME_FILE);
418: if (modfiles != null && modfiles.size() > 0) {
419:
420: Iterator it = modfiles.iterator();
421: while (it.hasNext()) {
422: Element modfileElement = (Element) it.next();
423: ModifiedFile modfile = new ModifiedFile(modfileElement,
424: formatter);
425: files.add(modfile);
426: }
427: }
428: }
429:
430: /**
431: * Concatenates the folderName and fileName of the Modification into a
432: * <code>String</code>. If the folderName is null then it is not included.
433: * All backward slashes ("\") are converted to forward slashes
434: * ("/").
435: *
436: * @return A <code>String</code> containing the full path
437: * of the modification
438: */
439: public String getFullPath() {
440: StringBuffer result = new StringBuffer();
441: String folderName = getFolderName();
442:
443: if (folderName != null) {
444: result.append(folderName).append("/");
445: }
446:
447: result.append(getFileName());
448: return result.toString().replace('\\', '/');
449: }
450:
451: public String getType() {
452: return type;
453: }
454:
455: public Date getModifiedTime() {
456: return modifiedTime;
457: }
458:
459: public String getUserName() {
460: return userName;
461: }
462:
463: public String getEmailAddress() {
464: return emailAddress;
465: }
466:
467: public String getRevision() {
468: return revision;
469: }
470:
471: public String getComment() {
472: return comment;
473: }
474:
475: }
|