# HG changeset patch # User Artem Tikhomirov # Date 1298579926 -3600 # Node ID ab7ea2ac21cbec118cba8d88d774d55f0784e19c # Parent 7a908ba66ff33d826cf3cffd4d4d645defe9b8b8 Format code diff -r 7a908ba66ff3 -r ab7ea2ac21cb src/org/tmatesoft/hg/repo/HgChangelog.java --- a/src/org/tmatesoft/hg/repo/HgChangelog.java Thu Feb 24 21:36:52 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/HgChangelog.java Thu Feb 24 21:38:46 2011 +0100 @@ -32,16 +32,15 @@ import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.RevlogStream; - /** * Representation of the Mercurial changelog file (list of ChangeSets) - * + * * @author Artem Tikhomirov * @author TMate Software Ltd. */ public class HgChangelog extends Revlog { - /*package-local*/ HgChangelog(HgRepository hgRepo, RevlogStream content) { + /* package-local */HgChangelog(HgRepository hgRepo, RevlogStream content) { super(hgRepo, content); } @@ -51,7 +50,7 @@ public void range(int start, int end, final HgChangelog.Inspector inspector) { RevlogStream.Inspector i = new RevlogStream.Inspector() { - + public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, byte[] data) { Changeset cset = Changeset.parse(data, 0, data.length); // XXX there's no guarantee for Changeset.Callback that distinct instance comes each time, consider instance reuse @@ -64,14 +63,14 @@ public List range(int start, int end) { final ArrayList rv = new ArrayList(end - start + 1); RevlogStream.Inspector i = new RevlogStream.Inspector() { - + public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, byte[] data) { Changeset cset = Changeset.parse(data, 0, data.length); rv.add(cset); } }; content.iterate(start, end, true, i); - return rv; + return rv; } public void range(final HgChangelog.Inspector inspector, final int... revisions) { @@ -79,7 +78,7 @@ return; } RevlogStream.Inspector i = new RevlogStream.Inspector() { - + public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, byte[] data) { if (Arrays.binarySearch(revisions, revisionNumber) >= 0) { Changeset cset = Changeset.parse(data, 0, data.length); @@ -91,219 +90,217 @@ content.iterate(revisions[0], revisions[revisions.length - 1], true, i); } - public interface Inspector { // TODO describe whether cset is new instance each time void next(int revisionNumber, Nodeid nodeid, Changeset cset); } - /** * Entry in the Changelog */ public static class Changeset implements Cloneable /* for those that would like to keep a copy */{ // TODO immutable private/* final */Nodeid manifest; - private String user; - private String comment; - private List files; // unmodifiable collection (otherwise #files() and implicit #clone() shall be revised) - private Date time; - private int timezone; - private Map extras; - - /** - * @see mercurial/changelog.py:read() - * - *
-			 *         format used:
-				 *         nodeid\n        : manifest node in ascii
-				 *         user\n          : user, no \n or \r allowed
-				 *         time tz extra\n : date (time is int or float, timezone is int)
-				 *                         : extra is metadatas, encoded and separated by '\0'
-				 *                         : older versions ignore it
-				 *         files\n\n       : files modified by the cset, no \n or \r allowed
-				 *         (.*)            : comment (free text, ideally utf-8)
-				 * 
-				 *         changelog v0 doesn't use extra
-				 * 
- */ - private Changeset() { - } - - public Nodeid manifest() { - return manifest; - } - - public String user() { - return user; - } - - public String comment() { - return comment; - } - - public List files() { - return files; - } - - public Date date() { - return time; - } - - public String dateString() { - // XXX keep once formatted? Perhaps, there's faster way to set up calendar/time zone? - StringBuilder sb = new StringBuilder(30); - Formatter f = new Formatter(sb, Locale.US); - TimeZone tz = TimeZone.getTimeZone("GMT"); - // apparently timezone field records number of seconds time differs from UTC, - // i.e. value to substract from time to get UTC time. Calendar seems to add - // timezone offset to UTC, instead, hence sign change. - tz.setRawOffset(timezone * -1000); - Calendar c = Calendar.getInstance(tz, Locale.US); - c.setTime(time); - f.format("%ta % extras() { - return extras; - } - - public String branch() { - return extras.get("branch"); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("Changeset {"); - sb.append("User: ").append(user).append(", "); - sb.append("Comment: ").append(comment).append(", "); - sb.append("Manifest: ").append(manifest).append(", "); - sb.append("Date: ").append(time).append(", "); - sb.append("Files: ").append(files.size()); - for (String s : files) { - sb.append(", ").append(s); - } - if (extras != null) { - sb.append(", Extra: ").append(extras); - } - sb.append("}"); - return sb.toString(); - } - - @Override - public Changeset clone() { - try { - return (Changeset) super.clone(); - } catch (CloneNotSupportedException ex) { - throw new InternalError(ex.toString()); - } - } + private String user; + private String comment; + private List files; // unmodifiable collection (otherwise #files() and implicit #clone() shall be revised) + private Date time; + private int timezone; + private Map extras; - public static Changeset parse(byte[] data, int offset, int length) { - Changeset rv = new Changeset(); - rv.init(data, offset, length); - return rv; - } + /** + * @see mercurial/changelog.py:read() + * + *
+		 *         format used:
+		 *         nodeid\n        : manifest node in ascii
+		 *         user\n          : user, no \n or \r allowed
+		 *         time tz extra\n : date (time is int or float, timezone is int)
+		 *                         : extra is metadatas, encoded and separated by '\0'
+		 *                         : older versions ignore it
+		 *         files\n\n       : files modified by the cset, no \n or \r allowed
+		 *         (.*)            : comment (free text, ideally utf-8)
+		 * 
+		 *         changelog v0 doesn't use extra
+		 * 
+ */ + private Changeset() { + } - /* package-local */void init(byte[] data, int offset, int length) { - final int bufferEndIndex = offset + length; - final byte lineBreak = (byte) '\n'; - int breakIndex1 = indexOf(data, lineBreak, offset, bufferEndIndex); - if (breakIndex1 == -1) { - throw new IllegalArgumentException("Bad Changeset data"); - } - Nodeid _nodeid = Nodeid.fromAscii(data, 0, breakIndex1); - int breakIndex2 = indexOf(data, lineBreak, breakIndex1 + 1, bufferEndIndex); - if (breakIndex2 == -1) { - throw new IllegalArgumentException("Bad Changeset data"); - } - String _user = new String(data, breakIndex1 + 1, breakIndex2 - breakIndex1 - 1); - int breakIndex3 = indexOf(data, lineBreak, breakIndex2 + 1, bufferEndIndex); - if (breakIndex3 == -1) { - throw new IllegalArgumentException("Bad Changeset data"); - } - String _timeString = new String(data, breakIndex2 + 1, breakIndex3 - breakIndex2 - 1); - int space1 = _timeString.indexOf(' '); - if (space1 == -1) { - throw new IllegalArgumentException("Bad Changeset data"); - } - int space2 = _timeString.indexOf(' ', space1 + 1); - if (space2 == -1) { - space2 = _timeString.length(); - } - long unixTime = Long.parseLong(_timeString.substring(0, space1)); // XXX Float, perhaps - int _timezone = Integer.parseInt(_timeString.substring(space1 + 1, space2)); - // XXX not sure need to add timezone here - I can't figure out whether Hg keeps GMT time, and records timezone just for info, or unixTime is taken local - // on commit and timezone is recorded to adjust it to UTC. - Date _time = new Date(unixTime * 1000); - String _extras = space2 < _timeString.length() ? _timeString.substring(space2 + 1) : null; - Map _extrasMap; - if (_extras == null) { - _extrasMap = Collections.singletonMap("branch", "default"); - } else { - _extrasMap = new HashMap(); - for (String pair : _extras.split("\00")) { - int eq = pair.indexOf(':'); - // FIXME need to decode key/value, @see changelog.py:decodeextra - _extrasMap.put(pair.substring(0, eq), pair.substring(eq + 1)); - } - if (!_extrasMap.containsKey("branch")) { - _extrasMap.put("branch", "default"); - } - _extrasMap = Collections.unmodifiableMap(_extrasMap); - } + public Nodeid manifest() { + return manifest; + } - // - int lastStart = breakIndex3 + 1; - int breakIndex4 = indexOf(data, lineBreak, lastStart, bufferEndIndex); - ArrayList _files = null; - if (breakIndex4 > lastStart) { - // if breakIndex4 == lastStart, we already found \n\n and hence there are no files (e.g. merge revision) - _files = new ArrayList(5); - while (breakIndex4 != -1 && breakIndex4 + 1 < bufferEndIndex) { - _files.add(new String(data, lastStart, breakIndex4 - lastStart)); - lastStart = breakIndex4 + 1; - if (data[breakIndex4 + 1] == lineBreak) { - // found \n\n - break; - } else { - breakIndex4 = indexOf(data, lineBreak, lastStart, bufferEndIndex); - } - } - if (breakIndex4 == -1 || breakIndex4 >= bufferEndIndex) { - throw new IllegalArgumentException("Bad Changeset data"); - } - } else { - breakIndex4--; - } - String _comment; - try { - _comment = new String(data, breakIndex4 + 2, bufferEndIndex - breakIndex4 - 2, "UTF-8"); - // FIXME respect ui.fallbackencoding and try to decode if set - } catch (UnsupportedEncodingException ex) { - _comment = ""; - throw new IllegalStateException("Could hardly happen"); - } - // change this instance at once, don't leave it partially changes in case of error - this.manifest = _nodeid; - this.user = _user; - this.time = _time; - this.timezone = _timezone; - this.files = _files == null ? Collections. emptyList() : Collections.unmodifiableList(_files); - this.comment = _comment; - this.extras = _extrasMap; + public String user() { + return user; + } + + public String comment() { + return comment; + } + + public List files() { + return files; + } + + public Date date() { + return time; + } + + public String dateString() { + // XXX keep once formatted? Perhaps, there's faster way to set up calendar/time zone? + StringBuilder sb = new StringBuilder(30); + Formatter f = new Formatter(sb, Locale.US); + TimeZone tz = TimeZone.getTimeZone("GMT"); + // apparently timezone field records number of seconds time differs from UTC, + // i.e. value to substract from time to get UTC time. Calendar seems to add + // timezone offset to UTC, instead, hence sign change. + tz.setRawOffset(timezone * -1000); + Calendar c = Calendar.getInstance(tz, Locale.US); + c.setTime(time); + f.format("%ta % extras() { + return extras; + } + + public String branch() { + return extras.get("branch"); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Changeset {"); + sb.append("User: ").append(user).append(", "); + sb.append("Comment: ").append(comment).append(", "); + sb.append("Manifest: ").append(manifest).append(", "); + sb.append("Date: ").append(time).append(", "); + sb.append("Files: ").append(files.size()); + for (String s : files) { + sb.append(", ").append(s); + } + if (extras != null) { + sb.append(", Extra: ").append(extras); + } + sb.append("}"); + return sb.toString(); + } + + @Override + public Changeset clone() { + try { + return (Changeset) super.clone(); + } catch (CloneNotSupportedException ex) { + throw new InternalError(ex.toString()); + } + } + + public static Changeset parse(byte[] data, int offset, int length) { + Changeset rv = new Changeset(); + rv.init(data, offset, length); + return rv; + } + + /* package-local */void init(byte[] data, int offset, int length) { + final int bufferEndIndex = offset + length; + final byte lineBreak = (byte) '\n'; + int breakIndex1 = indexOf(data, lineBreak, offset, bufferEndIndex); + if (breakIndex1 == -1) { + throw new IllegalArgumentException("Bad Changeset data"); + } + Nodeid _nodeid = Nodeid.fromAscii(data, 0, breakIndex1); + int breakIndex2 = indexOf(data, lineBreak, breakIndex1 + 1, bufferEndIndex); + if (breakIndex2 == -1) { + throw new IllegalArgumentException("Bad Changeset data"); + } + String _user = new String(data, breakIndex1 + 1, breakIndex2 - breakIndex1 - 1); + int breakIndex3 = indexOf(data, lineBreak, breakIndex2 + 1, bufferEndIndex); + if (breakIndex3 == -1) { + throw new IllegalArgumentException("Bad Changeset data"); + } + String _timeString = new String(data, breakIndex2 + 1, breakIndex3 - breakIndex2 - 1); + int space1 = _timeString.indexOf(' '); + if (space1 == -1) { + throw new IllegalArgumentException("Bad Changeset data"); + } + int space2 = _timeString.indexOf(' ', space1 + 1); + if (space2 == -1) { + space2 = _timeString.length(); + } + long unixTime = Long.parseLong(_timeString.substring(0, space1)); // XXX Float, perhaps + int _timezone = Integer.parseInt(_timeString.substring(space1 + 1, space2)); + // XXX not sure need to add timezone here - I can't figure out whether Hg keeps GMT time, and records timezone just for info, or unixTime is taken local + // on commit and timezone is recorded to adjust it to UTC. + Date _time = new Date(unixTime * 1000); + String _extras = space2 < _timeString.length() ? _timeString.substring(space2 + 1) : null; + Map _extrasMap; + if (_extras == null) { + _extrasMap = Collections.singletonMap("branch", "default"); + } else { + _extrasMap = new HashMap(); + for (String pair : _extras.split("\00")) { + int eq = pair.indexOf(':'); + // FIXME need to decode key/value, @see changelog.py:decodeextra + _extrasMap.put(pair.substring(0, eq), pair.substring(eq + 1)); + } + if (!_extrasMap.containsKey("branch")) { + _extrasMap.put("branch", "default"); + } + _extrasMap = Collections.unmodifiableMap(_extrasMap); + } + + // + int lastStart = breakIndex3 + 1; + int breakIndex4 = indexOf(data, lineBreak, lastStart, bufferEndIndex); + ArrayList _files = null; + if (breakIndex4 > lastStart) { + // if breakIndex4 == lastStart, we already found \n\n and hence there are no files (e.g. merge revision) + _files = new ArrayList(5); + while (breakIndex4 != -1 && breakIndex4 + 1 < bufferEndIndex) { + _files.add(new String(data, lastStart, breakIndex4 - lastStart)); + lastStart = breakIndex4 + 1; + if (data[breakIndex4 + 1] == lineBreak) { + // found \n\n + break; + } else { + breakIndex4 = indexOf(data, lineBreak, lastStart, bufferEndIndex); } + } + if (breakIndex4 == -1 || breakIndex4 >= bufferEndIndex) { + throw new IllegalArgumentException("Bad Changeset data"); + } + } else { + breakIndex4--; + } + String _comment; + try { + _comment = new String(data, breakIndex4 + 2, bufferEndIndex - breakIndex4 - 2, "UTF-8"); + // FIXME respect ui.fallbackencoding and try to decode if set + } catch (UnsupportedEncodingException ex) { + _comment = ""; + throw new IllegalStateException("Could hardly happen"); + } + // change this instance at once, don't leave it partially changes in case of error + this.manifest = _nodeid; + this.user = _user; + this.time = _time; + this.timezone = _timezone; + this.files = _files == null ? Collections. emptyList() : Collections.unmodifiableList(_files); + this.comment = _comment; + this.extras = _extrasMap; + } - private static int indexOf(byte[] src, byte what, int startOffset, int endIndex) { - for (int i = startOffset; i < endIndex; i++) { - if (src[i] == what) { - return i; - } - } - return -1; - } + private static int indexOf(byte[] src, byte what, int startOffset, int endIndex) { + for (int i = startOffset; i < endIndex; i++) { + if (src[i] == what) { + return i; + } + } + return -1; + } } }