1. Sebastian Sdorra
  2. hg4j

Commits

Artem Tikhomirov  committed 2fadf86

Use 'revision index' instead of the vague 'local revision number' concept in the API

  • Participants
  • Parent commits 189dc6d
  • Branches default

Comments (0)

Files changed (31)

File cmdline/org/tmatesoft/hg/console/ChangesetDumpHandler.java

View file
 		if (complete) {
 			Nodeid p1 = cset.getFirstParentRevision();
 			Nodeid p2 = cset.getSecondParentRevision();
-			int p1x = p1.isNull() ? -1 : repo.getChangelog().getLocalRevision(p1);
-			int p2x = p2.isNull() ? -1 : repo.getChangelog().getLocalRevision(p2);
-			int mx = repo.getManifest().getLocalRevision(cset.getManifestRevision());
+			int p1x = p1.isNull() ? -1 : repo.getChangelog().getRevisionIndex(p1);
+			int p2x = p2.isNull() ? -1 : repo.getChangelog().getRevisionIndex(p2);
+			int mx = repo.getManifest().getRevisionIndex(cset.getManifestRevision());
 			f.format("parent:      %d:%s\nparent:      %d:%s\nmanifest:    %d:%s\n", p1x, p1, p2x, p2, mx, cset.getManifestRevision());
 		}
 		f.format("user:        %s\ndate:        %s\n", cset.getUser(), cset.getDate().toString());

File cmdline/org/tmatesoft/hg/console/Main.java

View file
 	 	3 revisions - 80 ms vs 250 ms (250ms init)
 		4 revisions - 110 ms vs 265 ms (265 ms init)
 		5 revisions - 94 vs 266.
-		complete iteration in changelog.getLocalRevision(tipNodeid) takes 47 ms
+		complete iteration in changelog.getRevisionIndex(tipNodeid) takes 47 ms
 		compared to complete iteration inside RevisionMap.init() of 171 ms.
 		The only difference is latter instantiates Nodeids, while former compares binary content as is.
 		Hence, with 20-30 ms per regular getLocalRevision, it pays off to use RevisionMap with at least 15-20
 		revs[0] = changelog.getRevision(tip);
 		long start = System.currentTimeMillis();
 		for (int i = 0; i < revs.length; i++) {
-			final int localRev = changelog.getLocalRevision(revs[i]);
+			final int localRev = changelog.getRevisionIndex(revs[i]);
 			System.out.printf("%d:%s\n", localRev, revs[i]);
 		}
 		System.out.println(System.currentTimeMillis() - start);
 		rmap = changelog.new RevisionMap().init();
 		long s2 = System.currentTimeMillis();
 		for (int i = 0; i < revs.length; i++) {
-			final int localRev = rmap.localRevision(revs[i]);
+			final int localRev = rmap.revisionIndex(revs[i]);
 			System.out.printf("%d:%s\n", localRev, revs[i]);
 		}
 		System.out.println(System.currentTimeMillis() - start);

File cmdline/org/tmatesoft/hg/console/Tags.java

View file
 			}
 		});
 		for (TagInfo ti : tags.getTags().values()) {
-			int x = clog.getLocalRevision(ti.revision()); // XXX in fact, performance hog. Need batch localRevision or another improvement
+			int x = clog.getRevisionIndex(ti.revision()); // XXX in fact, performance hog. Need batch revisionIndex or another improvement
 			ti2index.put(ti, x);
 			sorted.add(ti);
 		}

File design.txt

View file
 
 Need better MethodRule than ErrorCollector for tests run as java app (to print not only MultipleFailureException, but distinct errors)
 Also consider using ExternalResource and TemporaryFolder rules. 
+
+
+=================
+Naming:
+nodeid: revision
+int:	revisionIndex (alternatives: revisionNumber, localRevisionNumber)
+BUT, if class name bears Revision, may use 'index' and 'nodeid'
+NOT nodeid because although fileNodeid and changesetNodeid are ok (less to my likening than fileRevision, however), it's not clear how
+to name integer counterpart, just 'index' is unclear, need to denote nodeid and index are related. 'nodeidIndex' would be odd.
+Unfortunately, Revision would be a nice name for a class <int, Nodeid>. As long as I don't want to keep methods to access int/nodeid separately
+and not to stick to Revision struct only (to avoid massive instances of Revision<int,Nodeid> when only one is sufficient), I'll need to name
+these separate methods anyway. Present opinion is that I don't need the object right now (will have to live with RevisionObject or RevisionDescriptor
+once change my mind) 

File src/org/tmatesoft/hg/core/ChangesetTransformer.java

View file
 			progressHelper.worked(1);
 			cancelHelper.checkCancelled();
 		} catch (RuntimeException ex) {
-			failure = new HgCallbackTargetException(ex).setRevision(nodeid).setRevisionNumber(revisionNumber);
+			failure = new HgCallbackTargetException(ex).setRevision(nodeid).setRevisionIndex(revisionNumber);
 		} catch (CancelledException ex) {
 			cancellation = ex;
 		}

File src/org/tmatesoft/hg/core/HgCallbackTargetException.java

View file
 		return (HgCallbackTargetException) super.setRevision(r);
 	}
 	@Override
-	public HgCallbackTargetException setRevisionNumber(int rev) {
-		return (HgCallbackTargetException) super.setRevisionNumber(rev);
+	public HgCallbackTargetException setRevisionIndex(int rev) {
+		return (HgCallbackTargetException) super.setRevisionIndex(rev);
 	}
 	@Override
 	public HgCallbackTargetException setFileName(Path name) {

File src/org/tmatesoft/hg/core/HgCatCommand.java

View file
  */
 package org.tmatesoft.hg.core;
 
-import static org.tmatesoft.hg.repo.HgInternals.wrongLocalRevision;
+import static org.tmatesoft.hg.repo.HgInternals.wrongRevisionIndex;
 import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION;
 import static org.tmatesoft.hg.repo.HgRepository.TIP;
 
 
 	private final HgRepository repo;
 	private Path file;
-	private int localRevision = TIP;
+	private int revisionIndex = TIP;
 	private Nodeid revision;
 	private Nodeid cset;
 
 	}
 
 	/**
-	 * Select specific local revision of the file to cat. Note, revision numbering is of particular file, not that of
+	 * Select specific revision of the file to cat with local revision index. Note, revision numbering is of particular file, not that of
 	 * repository (i.e. revision 0 means initial content of the file, irrespective of changeset revision at the time of commit) 
 	 * 
 	 * Invocation of this method clears revision set with {@link #revision(Nodeid)} or {@link #revision(int)} earlier.
 	 * 
-	 * XXX rev can't be WORKING_COPY (if allowed, need to implement in #execute())
-	 * @param rev local revision number, non-negative, or one of predefined constants. Note, use of {@link HgRepository#BAD_REVISION}, 
+	 * @param fileRevisionIndex local revision index, non-negative, or one of predefined constants. Note, use of {@link HgRepository#BAD_REVISION}, 
 	 * although possible, makes little sense (command would fail if executed).  
  	 * @return <code>this</code> for convenience
 	 */
-	public HgCatCommand revision(int rev) {
-		if (wrongLocalRevision(rev)) {
-			throw new IllegalArgumentException(String.valueOf(rev));
+	public HgCatCommand revision(int fileRevisionIndex) {
+		if (wrongRevisionIndex(fileRevisionIndex)) {
+			throw new IllegalArgumentException(String.valueOf(fileRevisionIndex));
 		}
-		localRevision = rev;
+		revisionIndex = fileRevisionIndex;
 		revision = null;
 		cset = null;
 		return this;
 	}
 	
 	/**
-	 * Select revision to read. Note, this revision is file revision (i.e. the one from manifest), not the changeset revision.
+	 * Select file revision to read. Note, this revision is file revision (i.e. the one from manifest), not the changeset revision.
 	 *  
 	 * Invocation of this method clears revision set with {@link #revision(int)} or {@link #revision(Nodeid)} earlier.
 	 * 
 			nodeid = null;
 		}
 		revision = nodeid;
-		localRevision = BAD_REVISION;
+		revisionIndex = BAD_REVISION;
 		cset = null;
 		return this;
 	}
 	 * @return <code>this</code> for convenience
 	 */
 	public HgCatCommand changeset(Nodeid nodeid) {
-		localRevision = BAD_REVISION;
+		revisionIndex = BAD_REVISION;
 		revision = null;
 		cset = nodeid;
 		return this;
 	 * @throws IllegalArgumentException when command arguments are incomplete or wrong
 	 */
 	public void execute(ByteChannel sink) throws HgDataStreamException, HgInvalidControlFileException, CancelledException {
-		if (localRevision == BAD_REVISION && revision == null && cset == null) {
+		if (revisionIndex == BAD_REVISION && revision == null && cset == null) {
 			throw new IllegalArgumentException("File revision, corresponing local number, or a changset nodeid shall be specified");
 		}
 		if (file == null) {
 		}
 		int revToExtract;
 		if (cset != null) {
-			int csetRev = repo.getChangelog().getLocalRevision(cset);
+			int csetRev = repo.getChangelog().getRevisionIndex(cset);
 			Nodeid toExtract = null;
 			do {
 				toExtract = repo.getManifest().getFileRevision(csetRev, file);
 			if (toExtract == null) {
 				throw new HgBadStateException(String.format("File %s nor its origins were not known at repository %s revision", file, cset.shortNotation()));
 			}
-			revToExtract = dataFile.getLocalRevision(toExtract);
+			revToExtract = dataFile.getRevisionIndex(toExtract);
 		} else if (revision != null) {
-			revToExtract = dataFile.getLocalRevision(revision);
+			revToExtract = dataFile.getRevisionIndex(revision);
 		} else {
-			revToExtract = localRevision;
+			revToExtract = revisionIndex;
 		}
 		ByteChannel sinkWrap;
 		if (getCancelSupport(null, false) == null) {

File src/org/tmatesoft/hg/core/HgDataStreamException.java

View file
 	}
 	
 	@Override
-	public HgDataStreamException setRevisionNumber(int rev) {
-		return (HgDataStreamException) super.setRevisionNumber(rev);
+	public HgDataStreamException setRevisionIndex(int rev) {
+		return (HgDataStreamException) super.setRevisionIndex(rev);
 	}
 	@Override
 	public HgDataStreamException setFileName(Path name) {

File src/org/tmatesoft/hg/core/HgException.java

View file
 	}
 
 	/**
-	 * @return not {@link HgRepository#BAD_REVISION} only when local revision number was supplied at the construction time
+	 * @return not {@link HgRepository#BAD_REVISION} only when local revision index was supplied at the construction time
 	 */
-	public int getRevisionNumber() {
+	public int getRevisionIndex() {
 		return revNumber;
 	}
 
-	public HgException setRevisionNumber(int rev) {
+	/**
+	 * @deprecated use {@link #getRevisionIndex()}
+	 */
+	@Deprecated
+	public int getRevisionNumber() {
+		return getRevisionIndex();
+	}
+	
+
+	public HgException setRevisionIndex(int rev) {
 		revNumber = rev;
 		return this;
 	}
+	
+	/**
+	 * @deprecated use {@link #setRevisionIndex(int)}
+	 */
+	@Deprecated
+	public final HgException setRevisionNumber(int rev) {
+		return setRevisionIndex(rev);
+	}
 
 	/**
 	 * @return non-null only when revision was supplied at construction time

File src/org/tmatesoft/hg/core/HgFileInformer.java

View file
 		Nodeid toExtract = null;
 		try {
 			if (cachedManifest == null) {
-				int csetRev = repo.getChangelog().getLocalRevision(cset);
+				int csetRev = repo.getChangelog().getRevisionIndex(cset);
 				cachedManifest = new ManifestRevision(null, null); // XXX how about context and cached manifest revisions
 				repo.getManifest().walk(csetRev, csetRev, cachedManifest);
-				// cachedManifest shall be meaningful - changelog.getLocalRevision above ensures we've got version that exists.
+				// cachedManifest shall be meaningful - changelog.getRevisionIndex() above ensures we've got version that exists.
 			}
 			toExtract = cachedManifest.nodeid(file);
 			if (toExtract == null && followRenames) {

File src/org/tmatesoft/hg/core/HgFileRevision.java

View file
 	public Pair<Nodeid, Nodeid> getParents() throws HgInvalidControlFileException {
 		if (parents == null) {
 			HgDataFile fn = repo.getFileNode(path);
-			int localRevision = fn.getLocalRevision(revision);
+			int revisionIndex = fn.getRevisionIndex(revision);
 			int[] pr = new int[2];
 			byte[] p1 = new byte[20], p2 = new byte[20];
 			// XXX Revlog#parents is not the best method to use here
 			// need smth that gives Nodeids (piped through Pool<Nodeid> from repo's context)
-			fn.parents(localRevision, pr, p1, p2);
+			fn.parents(revisionIndex, pr, p1, p2);
 			parents = new Pair<Nodeid, Nodeid>(Nodeid.fromBinary(p1, 0), Nodeid.fromBinary(p2, 0));
 		}
 		return parents;
 
 	public void putContentTo(ByteChannel sink) throws HgDataStreamException, HgInvalidControlFileException, CancelledException {
 		HgDataFile fn = repo.getFileNode(path);
-		int localRevision = fn.getLocalRevision(revision);
-		fn.contentWithFilters(localRevision, sink);
+		int revisionIndex = fn.getRevisionIndex(revision);
+		fn.contentWithFilters(revisionIndex, sink);
 	}
 
 	private void checkCopy() throws HgInvalidControlFileException, HgDataStreamException {

File src/org/tmatesoft/hg/core/HgLogCommand.java

View file
 	/**
 	 * Limit to specified subset of Changelog, [min(rev1,rev2), max(rev1,rev2)], inclusive.
 	 * Revision may be specified with {@link HgRepository#TIP}  
-	 * @param rev1 - local revision number
-	 * @param rev2 - local revision number
+	 * @param rev1 - local revision index
+	 * @param rev2 - local revision index
 	 * @return <code>this</code> instance for convenience
 	 */
 	public HgLogCommand range(int rev1, int rev2) {
 	 */
 	public HgLogCommand changeset(Nodeid nid) throws HgInvalidControlFileException, HgInvalidRevisionException {
 		// XXX perhaps, shall support multiple (...) arguments and extend #execute to handle not only range, but also set of revisions.
-		final int csetLocal = repo.getChangelog().getLocalRevision(nid);
+		final int csetLocal = repo.getChangelog().getRevisionIndex(nid);
 		return range(csetLocal, csetLocal);
 	}
 	

File src/org/tmatesoft/hg/core/HgManifestCommand.java

View file
 
 	/**
 	 * Parameterize command to visit revisions <code>[rev1..rev2]</code>.
-	 * @param rev1 - local revision number to start from. Non-negative. May be {@link HgRepository#TIP} (rev2 argument shall be {@link HgRepository#TIP} as well, then) 
-	 * @param rev2 - local revision number to end with, inclusive. Non-negative, greater or equal to rev1. May be {@link HgRepository#TIP}.
+	 * @param rev1 - local revision index to start from. Non-negative. May be {@link HgRepository#TIP} (rev2 argument shall be {@link HgRepository#TIP} as well, then) 
+	 * @param rev2 - local revision index to end with, inclusive. Non-negative, greater or equal to rev1. May be {@link HgRepository#TIP}.
 	 * @return <code>this</code> for convenience.
 	 * @throws IllegalArgumentException if revision arguments are incorrect (see above).
 	 */

File src/org/tmatesoft/hg/core/HgStatusCommand.java

View file
 package org.tmatesoft.hg.core;
 
 import static org.tmatesoft.hg.core.HgStatus.Kind.*;
-import static org.tmatesoft.hg.repo.HgInternals.wrongLocalRevision;
+import static org.tmatesoft.hg.repo.HgInternals.wrongRevisionIndex;
 import static org.tmatesoft.hg.repo.HgRepository.*;
 
 import java.io.IOException;
 	/**
 	 * If set, either base:revision or base:workingdir
 	 * to unset, pass {@link HgRepository#TIP} or {@link HgRepository#BAD_REVISION}
-	 * @param revision - local revision number to base status from
+	 * @param changesetRevisionIndex - local index of a changeset to base status from
 	 * @return <code>this</code> for convenience
 	 * @throws IllegalArgumentException when revision is negative or {@link HgRepository#WORKING_COPY} 
 	 */
-	public HgStatusCommand base(int revision) {
-		if (revision == WORKING_COPY || wrongLocalRevision(revision)) {
-			throw new IllegalArgumentException(String.valueOf(revision));
+	public HgStatusCommand base(int changesetRevisionIndex) {
+		if (changesetRevisionIndex == WORKING_COPY || wrongRevisionIndex(changesetRevisionIndex)) {
+			throw new IllegalArgumentException(String.valueOf(changesetRevisionIndex));
 		}
-		if (revision == BAD_REVISION) {
-			revision = TIP;
+		if (changesetRevisionIndex == BAD_REVISION) {
+			changesetRevisionIndex = TIP;
 		}
-		startRevision = revision;
+		startRevision = changesetRevisionIndex;
 		return this;
 	}
 	
 	/**
 	 * Revision without base == --change
 	 * Pass {@link HgRepository#WORKING_COPY} or {@link HgRepository#BAD_REVISION} to reset
-	 * @param revision - non-negative local revision number, or any of {@link HgRepository#BAD_REVISION}, {@link HgRepository#WORKING_COPY} or {@link HgRepository#TIP}  
+	 * @param changesetRevisionIndex - non-negative local revision number, or any of {@link HgRepository#BAD_REVISION}, {@link HgRepository#WORKING_COPY} or {@link HgRepository#TIP}  
 	 * @return <code>this</code> for convenience
 	 * @throws IllegalArgumentException if local revision number doesn't specify legitimate revision. 
 	 */
-	public HgStatusCommand revision(int revision) {
-		if (revision == BAD_REVISION) {
-			revision = WORKING_COPY;
+	public HgStatusCommand revision(int changesetRevisionIndex) {
+		if (changesetRevisionIndex == BAD_REVISION) {
+			changesetRevisionIndex = WORKING_COPY;
 		}
-		if (wrongLocalRevision(revision)) {
-			throw new IllegalArgumentException(String.valueOf(revision));
+		if (wrongRevisionIndex(changesetRevisionIndex)) {
+			throw new IllegalArgumentException(String.valueOf(changesetRevisionIndex));
 		}
-		endRevision = revision;
+		endRevision = changesetRevisionIndex;
 		return this;
 	}
 	
 			// seems too general to catch RuntimeException, i.e.
 			// unless catch is for very narrow piece of code, it's better not to catch any RTE (which may happen elsewhere, not only in handler)
 			// XXX Perhaps, need more detailed explanation in handlers that are expected to throw Wrap/RTE (i.e. HgChangesetHandler)
-			throw new HgCallbackTargetException(ex).setRevisionNumber(endRevision);
+			throw new HgCallbackTargetException(ex).setRevisionIndex(endRevision);
 		} finally {
 			mediator.done();
 		}

File src/org/tmatesoft/hg/internal/ChangelogHelper.java

View file
 		if (!df.exists()) {
 			return null;
 		}
-		int changelogRev = df.getChangesetLocalRevision(HgRepository.TIP);
+		int changelogRev = df.getChangesetRevisionIndex(HgRepository.TIP);
 		if (changelogRev >= leftBoundary) {
 			// the method is likely to be invoked for different files, 
 			// while changesets might be the same. Cache 'em not to read too much. 

File src/org/tmatesoft/hg/internal/KeywordFilter.java

View file
 	private String revision() {
 		try {
 			// FIXME add cset's nodeid into Changeset class
-			int csetRev = repo.getFileNode(path).getChangesetLocalRevision(HgRepository.TIP);
+			int csetRev = repo.getFileNode(path).getChangesetRevisionIndex(HgRepository.TIP);
 			return repo.getChangelog().getRevision(csetRev).shortNotation();
 		} catch (HgException ex) {
 			HgInternals.getContext(repo).getLog().error(getClass(), ex, null);
 	private RawChangeset getChangeset() throws HgInvalidControlFileException {
 		if (latestFileCset == null) {
 			// XXX consider use of ChangelogHelper
-			int csetRev = repo.getFileNode(path).getChangesetLocalRevision(HgRepository.TIP);
+			int csetRev = repo.getFileNode(path).getChangesetRevisionIndex(HgRepository.TIP);
 			latestFileCset = repo.getChangelog().range(csetRev, csetRev).get(0);
 		}
 		return latestFileCset;

File src/org/tmatesoft/hg/internal/RepositoryComparator.java

View file
 				// hence, we don't need to consider their local revision number
 				continue;
 			}
-			int lr = changelog.getLocalRevision(n);
+			int lr = changelog.getRevisionIndex(n);
 			if (lr < earliestRevision) {
 				earliestRevision = lr;
 			}

File src/org/tmatesoft/hg/internal/RevlogStream.java

View file
 	 * @return integer in [0..revisionCount()) or {@link HgRepository#BAD_REVISION} if not found
 	 * @throws HgInvalidControlFileException if attempt to read index file failed
 	 */
-	public int findLocalRevisionNumber(Nodeid nodeid) throws HgInvalidControlFileException {
+	public int findRevisionIndex(Nodeid nodeid) throws HgInvalidControlFileException {
 		// XXX this one may be implemented with iterate() once there's mechanism to stop iterations
 		final int indexSize = revisionCount();
 		DataAccess daIndex = getIndexStream();
 		// XXX boolean retVal to indicate whether to continue?
 		// TODO specify nodeid and data length, and reuse policy (i.e. if revlog stream doesn't reuse nodeid[] for each call)
 		// implementers shall not invoke DataAccess.done(), it's accomplished by #iterate at appropraite moment
-		void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[/*20*/] nodeid, DataAccess data) throws HgException;
+		void next(int revisionIndex, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[/*20*/] nodeid, DataAccess data) throws HgException;
 	}
 }

File src/org/tmatesoft/hg/repo/HgBranches.java

View file
 			int[] localCset = new int[heads.size()];
 			int i = 0;
 			for (Nodeid h : heads) {
-				localCset[i++] = rmap.localRevision(h);
+				localCset[i++] = rmap.revisionIndex(h);
 			}
 			// [0] tipmost, [1] tipmost open
 			final Nodeid[] tipmost = new Nodeid[] {null, null};

File src/org/tmatesoft/hg/repo/HgChangelog.java

View file
 	 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
 	 */
 	public RawChangeset changeset(Nodeid nid)  throws HgInvalidControlFileException, HgInvalidRevisionException {
-		int x = getLocalRevision(nid);
+		int x = getRevisionIndex(nid);
 		return range(x, x).get(0);
 	}
 

File src/org/tmatesoft/hg/repo/HgDataFile.java

View file
  */
 package org.tmatesoft.hg.repo;
 
-import static org.tmatesoft.hg.repo.HgInternals.wrongLocalRevision;
+import static org.tmatesoft.hg.repo.HgInternals.wrongRevisionIndex;
 import static org.tmatesoft.hg.repo.HgRepository.*;
 
 import java.io.ByteArrayOutputStream;
 	}
 
 	/**
-	 * Handy shorthand for {@link #length(int) length(getLocalRevision(nodeid))}
+	 * Handy shorthand for {@link #length(int) length(getRevisionIndex(nodeid))}
 	 *
 	 * @param nodeid revision of the file
 	 * 
 	 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
 	 */
 	public int length(Nodeid nodeid) throws HgDataStreamException, HgInvalidControlFileException, HgInvalidRevisionException {
-		return length(getLocalRevision(nodeid));
+		return length(getRevisionIndex(nodeid));
 	}
 	
 	/**
+ 	 * @param fileRevisionIndex local revision index, non-negative. From predefined constants, only {@link HgRepository#TIP} makes sense. 
 	 * @return size of the file content at the revision identified by local revision number.
 	 * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog
 	 * @throws HgDataStreamException if attempt to access file metadata failed
 	 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
 	 */
-	public int length(int localRev) throws HgDataStreamException, HgInvalidControlFileException, HgInvalidRevisionException {
-		if (metadata == null || !metadata.checked(localRev)) {
-			checkAndRecordMetadata(localRev);
+	public int length(int fileRevisionIndex) throws HgDataStreamException, HgInvalidControlFileException, HgInvalidRevisionException {
+		// TODO support WORKING_COPY constant
+		if (metadata == null || !metadata.checked(fileRevisionIndex)) {
+			checkAndRecordMetadata(fileRevisionIndex);
 		}
-		final int dataLen = content.dataLength(localRev);
-		if (metadata.known(localRev)) {
-			return dataLen - metadata.dataOffset(localRev);
+		final int dataLen = content.dataLength(fileRevisionIndex);
+		if (metadata.known(fileRevisionIndex)) {
+			return dataLen - metadata.dataOffset(fileRevisionIndex);
 		}
 		return dataLen;
 	}
 		}
 	}
 
-	// for data files need to check heading of the file content for possible metadata
-	// @see http://mercurial.selenic.com/wiki/FileFormats#data.2BAC8-
-	public void content(int revision, ByteChannel sink) throws HgDataStreamException, HgInvalidControlFileException, CancelledException, HgInvalidRevisionException {
-		if (revision == TIP) {
-			revision = getLastRevision();
+	/**
+	 * 
+ 	 * @param fileRevisionIndex local revision index, non-negative. From predefined constants, {@link HgRepository#TIP} and {@link HgRepository#WORKING_COPY} make sense. 
+	 * @param sink
+	 * @throws HgDataStreamException FIXME
+	 * @throws HgInvalidControlFileException
+	 * @throws CancelledException
+	 * @throws HgInvalidRevisionException
+	 */
+	public void content(int fileRevisionIndex, ByteChannel sink) throws HgDataStreamException, HgInvalidControlFileException, CancelledException, HgInvalidRevisionException {
+		// for data files need to check heading of the file content for possible metadata
+		// @see http://mercurial.selenic.com/wiki/FileFormats#data.2BAC8-
+		if (fileRevisionIndex == TIP) {
+			fileRevisionIndex = getLastRevision();
 		}
-		if (revision == WORKING_COPY) {
+		if (fileRevisionIndex == WORKING_COPY) {
 			// sink is supposed to come into workingCopy without filters
 			// thus we shall not get here (into #content) from #contentWithFilters(WC)
 			workingCopy(sink);
 			return;
 		}
-		if (wrongLocalRevision(revision) || revision == BAD_REVISION) {
-			throw new HgInvalidRevisionException(revision);
+		if (wrongRevisionIndex(fileRevisionIndex) || fileRevisionIndex == BAD_REVISION) {
+			throw new HgInvalidRevisionException(fileRevisionIndex);
 		}
 		if (sink == null) {
 			throw new IllegalArgumentException();
 			metadata = new Metadata();
 		}
 		ErrorHandlingInspector insp;
-		if (metadata.none(revision)) {
+		if (metadata.none(fileRevisionIndex)) {
 			insp = new ContentPipe(sink, 0, getRepo().getContext().getLog());
-		} else if (metadata.known(revision)) {
-			insp = new ContentPipe(sink, metadata.dataOffset(revision), getRepo().getContext().getLog());
+		} else if (metadata.known(fileRevisionIndex)) {
+			insp = new ContentPipe(sink, metadata.dataOffset(fileRevisionIndex), getRepo().getContext().getLog());
 		} else {
 			// do not know if there's metadata
 			insp = new MetadataInspector(metadata, getPath(), new ContentPipe(sink, 0, getRepo().getContext().getLog()));
 		}
 		insp.checkCancelled();
-		super.content.iterate(revision, revision, true, insp);
+		super.content.iterate(fileRevisionIndex, fileRevisionIndex, true, insp);
 		try {
 			insp.checkFailed(); // XXX is there real need to throw IOException from ContentPipe?
 		} catch (HgDataStreamException ex) {
 			throw ex;
 		} catch (IOException ex) {
-			throw new HgDataStreamException(getPath(), ex).setRevisionNumber(revision);
+			throw new HgDataStreamException(getPath(), ex).setRevisionIndex(fileRevisionIndex);
 		} catch (HgException ex) {
 			// shall not happen, unless we changed ContentPipe or its subclass
 			throw new HgDataStreamException(getPath(), ex.getClass().getName(), ex);
 	}
 	
 	/**
-	 * For a given local revision of the file, find out local revision in the changelog.
-	 * FIXME rename to getChangesetRevisionIndex()
+	 * For a given revision of the file (identified with revision index), find out index of the corresponding changeset.
 	 *
 	 * @return changeset revision index
 	 * @throws HgInvalidRevisionException if supplied argument doesn't represent revision index in this revlog
 	 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
 	 */
+	public int getChangesetRevisionIndex(int revision) throws HgInvalidControlFileException, HgInvalidRevisionException {
+		return content.linkRevision(revision);
+	}
+	/**
+	 * @deprecated use {@link #getChangesetRevisionIndex(int)} instead
+	 */
+	@Deprecated
 	public int getChangesetLocalRevision(int revision) throws HgInvalidControlFileException, HgInvalidRevisionException {
-		return content.linkRevision(revision);
+		return getChangesetRevisionIndex(revision);
 	}
 
 	/**
-	 * Complements {@link #getChangesetLocalRevision(int)} to get changeset revision that corresponds to supplied file revision
+	 * Complements {@link #getChangesetRevisionIndex(int)} to get changeset revision that corresponds to supplied file revision
 	 * 
 	 * @param nid revision of the file
 	 * @return changeset revision
 	 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
 	 */
 	public Nodeid getChangesetRevision(Nodeid nid) throws HgInvalidControlFileException, HgInvalidRevisionException {
-		int changelogRevision = getChangesetLocalRevision(getLocalRevision(nid));
+		int changelogRevision = getChangesetRevisionIndex(getRevisionIndex(nid));
 		return getRepo().getChangelog().getRevision(changelogRevision);
 	}
 
 			} catch (IOException ex) {
 				recordFailure(ex);
 			} catch (HgDataStreamException ex) {
-				recordFailure(ex.setRevisionNumber(revisionNumber));
+				recordFailure(ex.setRevisionIndex(revisionNumber));
 			}
 		}
 

File src/org/tmatesoft/hg/repo/HgInternals.java

View file
 
 
 	// Convenient check of local revision number for validity (not all negative values are wrong as long as we use negative constants)
-	public static boolean wrongLocalRevision(int rev) {
+	public static boolean wrongRevisionIndex(int rev) {
 		return rev < 0 && rev != TIP && rev != WORKING_COPY && rev != BAD_REVISION; 
 	}
 

File src/org/tmatesoft/hg/repo/HgManifest.java

View file
 	 * "Sparse" iteration of the manifest
 	 * 
 	 * @param inspector
-	 * @param localRevisions local changeset revisions to visit
+	 * @param revisionIndexes local indexes of changesets to visit
 	 */
-	public void walk(final Inspector inspector, int... localRevisions) throws HgInvalidControlFileException{
-		if (inspector == null || localRevisions == null) {
+	public void walk(final Inspector inspector, int... revisionIndexes) throws HgInvalidControlFileException{
+		if (inspector == null || revisionIndexes == null) {
 			throw new IllegalArgumentException();
 		}
-		int[] localManifestRevs = toLocalManifestRevisions(localRevisions);
+		int[] localManifestRevs = toManifestRevisionIndexes(revisionIndexes);
 		content.iterate(localManifestRevs, true, new ManifestParser(inspector));
 	}
 	
 	// manifest revision number that corresponds to the given changeset
 	/*package-local*/ int fromChangelog(int revisionNumber) throws HgInvalidControlFileException {
-		if (HgInternals.wrongLocalRevision(revisionNumber)) {
+		if (HgInternals.wrongRevisionIndex(revisionNumber)) {
 			throw new IllegalArgumentException(String.valueOf(revisionNumber));
 		}
 		if (revisionNumber == HgRepository.WORKING_COPY || revisionNumber == HgRepository.BAD_REVISION) {
 	/**
 	 * Extracts file revision as it was known at the time of given changeset.
 	 * 
-	 * @param localChangelogRevision local changeset index 
+	 * @param changelogRevisionIndex local changeset index 
 	 * @param file path to file in question
 	 * @return file revision or <code>null</code> if manifest at specified revision doesn't list such file
 	 */
 	@Experimental(reason="Perhaps, HgDataFile shall own this method, or get a delegate?")
-	public Nodeid getFileRevision(int localChangelogRevision, final Path file) throws HgInvalidControlFileException{
-		return getFileRevisions(file, localChangelogRevision).get(localChangelogRevision);
+	public Nodeid getFileRevision(int changelogRevisionIndex, final Path file) throws HgInvalidControlFileException{
+		return getFileRevisions(file, changelogRevisionIndex).get(changelogRevisionIndex);
 	}
 	
 	// XXX package-local, IntMap, and HgDataFile getFileRevisionAt(int... localChangelogRevisions)
 	@Experimental(reason="@see #getFileRevision")
-	public Map<Integer, Nodeid> getFileRevisions(final Path file, int... localChangelogRevisions) throws HgInvalidControlFileException{
+	public Map<Integer, Nodeid> getFileRevisions(final Path file, int... changelogRevisionIndexes) throws HgInvalidControlFileException{
 		// FIXME need tests
-		int[] localManifestRevisions = toLocalManifestRevisions(localChangelogRevisions);
-		final HashMap<Integer,Nodeid> rv = new HashMap<Integer, Nodeid>(localChangelogRevisions.length);
-		content.iterate(localManifestRevisions, true, new RevlogStream.Inspector() {
+		int[] manifestRevisionIndexes = toManifestRevisionIndexes(changelogRevisionIndexes);
+		final HashMap<Integer,Nodeid> rv = new HashMap<Integer, Nodeid>(changelogRevisionIndexes.length);
+		content.iterate(manifestRevisionIndexes, true, new RevlogStream.Inspector() {
 			
 			public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) throws HgException {
 				ByteArrayOutputStream bos = new ByteArrayOutputStream();
 	}
 
 
-	private int[] toLocalManifestRevisions(int[] localChangelogRevisions) throws HgInvalidControlFileException {
-		int[] localManifestRevs = new int[localChangelogRevisions.length];
+	private int[] toManifestRevisionIndexes(int[] changelogRevisionIndexes) throws HgInvalidControlFileException {
+		int[] localManifestRevs = new int[changelogRevisionIndexes.length];
 		boolean needsSort = false;
-		for (int i = 0; i < localChangelogRevisions.length; i++) {
-			final int manifestLocalRev = fromChangelog(localChangelogRevisions[i]);
-			localManifestRevs[i] = manifestLocalRev;
-			if (i > 0 && localManifestRevs[i-1] > manifestLocalRev) {
+		for (int i = 0; i < changelogRevisionIndexes.length; i++) {
+			final int manifestRevisionIndex = fromChangelog(changelogRevisionIndexes[i]);
+			localManifestRevs[i] = manifestRevisionIndex;
+			if (i > 0 && localManifestRevs[i-1] > manifestRevisionIndex) {
 				needsSort = true;
 			}
 		}
 					Nodeid manifest = repo.getChangelog().range(u, u).get(0).manifest();
 					// FIXME calculate those missing effectively (e.g. cache and sort nodeids to speed lookup
 					// right away in the #next (may refactor ParentWalker's sequential and sorted into dedicated helper and reuse here)
-					changelog2manifest[u] = repo.getManifest().getLocalRevision(manifest);
+					changelog2manifest[u] = repo.getManifest().getRevisionIndex(manifest);
 				} catch (HgInvalidControlFileException ex) {
 					// FIXME need to propagate the error up to client  
 					repo.getContext().getLog().error(getClass(), ex, null);

File src/org/tmatesoft/hg/repo/HgMergeState.java

View file
 			final ManifestRevision m1 = new ManifestRevision(nodeidPool, fnamePool);
 			final ManifestRevision m2 = new ManifestRevision(nodeidPool, fnamePool);
 			if (!wcp2.isNull()) {
-				final int rp2 = repo.getChangelog().getLocalRevision(wcp2);
+				final int rp2 = repo.getChangelog().getRevisionIndex(wcp2);
 				repo.getManifest().walk(rp2, rp2, m2);
 			}
 			BufferedReader br = new BufferedReader(new FileReader(f));
 			String s = br.readLine();
 			stateParent = nodeidPool.unify(Nodeid.fromAscii(s));
-			final int rp1 = repo.getChangelog().getLocalRevision(stateParent);
+			final int rp1 = repo.getChangelog().getRevisionIndex(stateParent);
 			repo.getManifest().walk(rp1, rp1, m1);
 			while ((s = br.readLine()) != null) {
 				String[] r = s.split("\\00");

File src/org/tmatesoft/hg/repo/HgRepository.java

View file
  */
 public final class HgRepository {
 
-	// if new constants added, consider fixing HgInternals#wrongLocalRevision
+	// if new constants added, consider fixing HgInternals#wrongRevisionIndex
 	public static final int TIP = -3;
 	public static final int BAD_REVISION = Integer.MIN_VALUE;
 	public static final int WORKING_COPY = -2;

File src/org/tmatesoft/hg/repo/HgStatusCollector.java

View file
 		HgDataFile df = hgRepo.getFileNode(fname);
 		if (!df.exists()) {
 			String msg = String.format("Didn't find file '%s' in the repo. Perhaps, bad storage name conversion?", fname);
-			throw new HgDataStreamException(fname, msg, null).setRevisionNumber(originalChangelogRevision);
+			throw new HgDataStreamException(fname, msg, null).setRevisionIndex(originalChangelogRevision);
 		}
 		while (df.isCopy()) {
 			Path original = df.getCopySourceName();
 			if (originals.contains(original)) {
 				df = hgRepo.getFileNode(original);
-				int changelogRevision = df.getChangesetLocalRevision(0);
+				int changelogRevision = df.getChangesetRevisionIndex(0);
 				if (changelogRevision <= originalChangelogRevision) {
 					// copy/rename source was known prior to rev1 
 					// (both r1Files.contains is true and original was created earlier than rev1)

File src/org/tmatesoft/hg/repo/HgTags.java

View file
 
 		public String branch() throws HgInvalidControlFileException {
 			if (branch == null) {
-				int x = repo.getChangelog().getLocalRevision(revision());
+				int x = repo.getChangelog().getRevisionIndex(revision());
 				branch = repo.getChangelog().range(x, x).get(0).branch();
 			}
 			return branch;

File src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java

View file
 		if (dirstateParent.isNull()) {
 			dirstateParentManifest = baseRevisionCollector != null ? baseRevisionCollector.raw(-1) : HgStatusCollector.createEmptyManifestRevision();
 		} else {
-			int changelogLocalRev = repo.getChangelog().getLocalRevision(dirstateParent);
-			dirstateParentManifest = getManifest(changelogLocalRev);
+			int changeloRevIndex = repo.getChangelog().getRevisionIndex(dirstateParent);
+			dirstateParentManifest = getManifest(changeloRevIndex);
 		}
 	}
 
 	// may be invoked few times, TIP or WORKING_COPY indicate comparison shall be run against working copy parent
 	// NOTE, use of TIP constant requires certain care. TIP here doesn't mean latest cset, but actual working copy parent.
 	public void walk(int baseRevision, HgStatusInspector inspector) throws HgInvalidControlFileException, IOException {
-		if (HgInternals.wrongLocalRevision(baseRevision) || baseRevision == BAD_REVISION) {
+		if (HgInternals.wrongRevisionIndex(baseRevision) || baseRevision == BAD_REVISION) {
 			throw new IllegalArgumentException(String.valueOf(baseRevision));
 		}
 		if (getDirstateImpl() == null) {
 		ByteArrayChannel bac = new ByteArrayChannel();
 		boolean ioFailed = false;
 		try {
-			int localRevision = dataFile.getLocalRevision(revision);
+			int fileRevisionIndex = dataFile.getRevisionIndex(revision);
 			// need content with metadata striped off - although theoretically chances are metadata may be different,
 			// WC doesn't have it anyway 
-			dataFile.content(localRevision, bac);
+			dataFile.content(fileRevisionIndex, bac);
 		} catch (CancelledException ex) {
 			// silently ignore - can't happen, ByteArrayChannel is not cancellable
 		} catch (HgException ex) {

File src/org/tmatesoft/hg/repo/Revlog.java

View file
 	 * @throws HgInvalidRevisionException if supplied nodeid doesn't identify any revision from this revlog  
 	 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
 	 */
-	public final int getLocalRevision(Nodeid nid) throws HgInvalidControlFileException, HgInvalidRevisionException {
-		int revision = content.findLocalRevisionNumber(nid);
+	public final int getRevisionIndex(Nodeid nid) throws HgInvalidControlFileException, HgInvalidRevisionException {
+		int revision = content.findRevisionIndex(nid);
 		if (revision == BAD_REVISION) {
 			throw new HgInvalidRevisionException(String.format("Bad revision of %s", this /*XXX HgDataFile.getPath might be more suitable here*/), nid, null);
 		}
 		return revision;
 	}
+	
+	/**
+	 * @deprecated use {@link #getRevisionIndex(Nodeid)} instead
+	 */
+	@Deprecated
+	public final int getLocalRevision(Nodeid nid) throws HgInvalidControlFileException, HgInvalidRevisionException {
+		return getRevisionIndex(nid);
+	}
+
 
 	/**
 	 * Note, {@link Nodeid#NULL} nodeid is not reported as known in any revlog.
 	 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
 	 */
 	public final boolean isKnown(Nodeid nodeid) throws HgInvalidControlFileException {
-		final int rn = content.findLocalRevisionNumber(nodeid);
+		final int rn = content.findRevisionIndex(nodeid);
 		if (BAD_REVISION == rn) {
 			return false;
 		}
 	 * @param nodeid
 	 */
 	protected void rawContent(Nodeid nodeid, ByteChannel sink) throws HgException, IOException, CancelledException, HgInvalidRevisionException {
-		rawContent(getLocalRevision(nodeid), sink);
+		rawContent(getRevisionIndex(nodeid), sink);
 	}
 	
 	/**
 
 	@Experimental
 	public interface RevisionInspector extends Inspector {
-		void next(int localRevision, Nodeid revision, int linkedRevision);
+		void next(int revisionIndex, Nodeid revision, int linkedRevision);
 	}
 
 	@Experimental
 	public interface ParentInspector extends Inspector {
 		// XXX document whether parentX is -1 or a constant (BAD_REVISION? or dedicated?)
-		void next(int localRevision, Nodeid revision, int parent1, int parent2, Nodeid nidParent1, Nodeid nidParent2);
+		void next(int revisionIndex, Nodeid revision, int parent1, int parent2, Nodeid nidParent1, Nodeid nidParent2);
 	}
 	
 	/*
 
 	/**
 	 * Effective int to Nodeid and vice versa translation. It's advised to use this class instead of 
-	 * multiple {@link Revlog#getLocalRevision(Nodeid)} calls.
+	 * multiple {@link Revlog#getRevisionIndex(Nodeid)} calls.
 	 * 
-	 * getLocalRevision(Nodeid) with straightforward lookup approach performs O(n/2)
+	 * getRevisionIndex(Nodeid) with straightforward lookup approach performs O(n/2)
 	 * #localRevision() is log(n), plus initialization is O(n) 
 	 */
 	public final class RevisionMap implements RevisionInspector {
 			return Revlog.this.getRepo();
 		}
 		
-		public void next(int localRevision, Nodeid revision, int linkedRevision) {
-			sequential[localRevision] = sorted[localRevision] = revision;
+		public void next(int revisionIndex, Nodeid revision, int linkedRevision) {
+			sequential[revisionIndex] = sorted[revisionIndex] = revision;
 		}
 
 		/**
 			return this;
 		}
 		
-		public Nodeid revision(int localRevision) {
-			return sequential[localRevision];
+		public Nodeid revision(int revisionIndex) {
+			return sequential[revisionIndex];
 		}
-		public int localRevision(Nodeid revision) {
+		public int revisionIndex(Nodeid revision) {
 			if (revision == null || revision.isNull()) {
 				return BAD_REVISION;
 			}
 			}
 			return sorted2natural[x]-1;
 		}
+		/**
+		 * @deprecated use {@link #revisionIndex(Nodeid)} instead
+		 */
+		@Deprecated
+		public int localRevision(Nodeid revision) {
+			return revisionIndex(revision);
+		}
 	}
 
 	protected abstract static class ErrorHandlingInspector implements RevlogStream.Inspector, CancelSupport {

File test/org/tmatesoft/hg/test/MapTagsToFileRevisions.java

View file
 		for (int revision = 0; revision <= latestRevision; revision++) {
 		       final Nodeid nodeId = fileMap.revision(revision);
 //		       final Nodeid changesetId = fileNode.getChangesetRevision(nodeId);
-		       int localCset = fileNode.getChangesetLocalRevision(revision);
+		       int localCset = fileNode.getChangesetRevisionIndex(revision);
 		       final Nodeid changesetId = clog.getRevision(localCset);
 		       changesetToNodeid_1.put(changesetId, nodeId);
 		}
 		final long start_2a = System.nanoTime();
 		for (int revision = 0; revision <= latestRevision; revision++) {
 			Nodeid nidFile = fileMap.revision(revision);
-			int localCset = fileNode.getChangesetLocalRevision(revision);
+			int localCset = fileNode.getChangesetRevisionIndex(revision);
 			Nodeid nidCset = clogMap.revision(localCset);
 			changesetToNodeid_2.put(nidCset, nidFile);
 		}
 
 	/*
 	 * Each 5000 revisions from cpython, total 15 revisions
-	 * Direct clog.getLocalRevision: ~260 ms
-	 * RevisionMap.localRevision: ~265 ms (almost 100% in #init())
+	 * Direct clog.getRevisionIndex: ~260 ms
+	 * RevisionMap.revisionIndex: ~265 ms (almost 100% in #init())
 	 * each 1000'th revision, total 71 revision: 1 230 vs 270
 	 * each 2000'th revision, total 36 revision: 620 vs 270
 	 * each 3000'th revision, total 24 revision: 410 vs 275
 		}
 		final long s1 = System.nanoTime();
 		for (Nodeid n : revisions) {
-			int r = clog.getLocalRevision(n);
+			int r = clog.getRevisionIndex(n);
 			if (r % step != 0) {
 				throw new IllegalStateException(Integer.toString(r));
 			}
 		rmap.init();
 		final long s3 = System.nanoTime();
 		for (Nodeid n : revisions) {
-			int r = rmap.localRevision(n);
+			int r = rmap.revisionIndex(n);
 			if (r % step != 0) {
 				throw new IllegalStateException(Integer.toString(r));
 			}
 		int x = 0;
 		for (int i = 0; i < allTags.length; i++) {
 			final Nodeid tagRevision = allTags[i].revision();
-			final int tagLocalRev = clogrmap.localRevision(tagRevision);
+			final int tagLocalRev = clogrmap.revisionIndex(tagRevision);
 			if (tagLocalRev != HgRepository.BAD_REVISION) {
 				tagLocalRevs[x++] = tagLocalRev;
 				List<TagInfo> tagsAssociatedWithRevision = tagLocalRev2TagInfo.get(tagLocalRev);
 		// TODO if fileNode.isCopy, repeat for each getCopySourceName()
 		for (int localFileRev = 0; localFileRev < fileNode.getRevisionCount(); localFileRev++) {
 			Nodeid fileRev = fileNode.getRevision(localFileRev);
-			int changesetLocalRev = fileNode.getChangesetLocalRevision(localFileRev);
+			int changesetLocalRev = fileNode.getChangesetRevisionIndex(localFileRev);
 			List<String> associatedTags = new LinkedList<String>();
 			for (int i = 0; i < allTagsOfTheFile.length; i++) {
 				if (fileRev.equals(allTagsOfTheFile[i])) {
 			final HgTags.TagInfo info = tagToInfo.get(tagName);
 			final Nodeid nodeId = info.revision();
 			// TODO: This is not correct as we can't be sure that file at the corresponding revision is actually our target file (which may have been renamed, etc.)
-			final Nodeid fileRevision = manifest.getFileRevision(repository.getChangelog().getLocalRevision(nodeId), targetPath);
+			final Nodeid fileRevision = manifest.getFileRevision(repository.getChangelog().getRevisionIndex(nodeId), targetPath);
 			if (fileRevision == null) {
 				continue;
 			}

File test/org/tmatesoft/hg/test/TestAuxUtilities.java

View file
 			public void next(int localRevision, Nodeid revision, int linkedRevision) {
 				try {
 					Assert.assertEquals(i++, localRevision);
-					Assert.assertEquals(fileNode.getChangesetLocalRevision(localRevision), linkedRevision);
+					Assert.assertEquals(fileNode.getChangesetRevisionIndex(localRevision), linkedRevision);
 					Assert.assertEquals(fileNode.getRevision(localRevision), revision);
 				} catch (HgException ex) {
 					Assert.fail(ex.toString());