You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by br...@apache.org on 2013/07/05 02:53:02 UTC
svn commit: r1499893 - in /subversion/trunk/subversion/bindings/javahl:
src/org/apache/subversion/javahl/callback/RemoteStatus.java
src/org/apache/subversion/javahl/remote/StatusEditor.java
tests/org/apache/subversion/javahl/SVNRemoteTests.java
Author: brane
Date: Fri Jul 5 00:53:02 2013
New Revision: 1499893
URL: http://svn.apache.org/r1499893
Log:
Extend the JavaHL remote status interface so that it provides info gleaned
from entry props for modified nodes.
[in subversion/bindings/javahl/src/org/apache/subversion/javahl]
* callback/RemoteStatus.java (RemoteStatus.Entry): New class.
(RemoteStatus.modifiedDirectory,
RemoteStatus.modifiedFile,
RemoteStatus.modifiedSymlink): Add nodeInfo parameter to callback method.
* remote/StatusEditor.java
(StatusEditor.alterDirectory,
StatusEditor.alterFile,
StatusEditor.alterSymlink): Update RemoteStatus driver methods.
(StatusEditor.make_entry): New; constructs an Entry from entry props.
[in subversion/bindings/javahl/tests/org/apache/subversion/javahl]
* SVNRemoteTests.java (SVNRemoteTests.RemoteStatusReceiver): Add node info.
Add optional output to stderr to debug status info.
(SVNRemoteTests.testPropchangeStatus): Check deleted flag and entry revision.
(SVNRemoteTests.testDeletedStatus): New test case.
Modified:
subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/RemoteStatus.java
subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/StatusEditor.java
subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java
Modified: subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/RemoteStatus.java
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/RemoteStatus.java?rev=1499893&r1=1499892&r2=1499893&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/RemoteStatus.java (original)
+++ subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/RemoteStatus.java Fri Jul 5 00:53:02 2013
@@ -58,34 +58,130 @@ public interface RemoteStatus
* @param relativePath The session-relative path of the directory.
* @param childrenModified The directory contents changed.
* @param propsModified The directory's properties changed.
+ * @param nodeInfo Additional information about the modified directory.
*/
void modifiedDirectory(String relativePath,
boolean childrenModified,
- boolean propsModified);
+ boolean propsModified,
+ Entry nodeInfo);
/**
* A file was modified.
* @param relativePath The session-relative path of the directory.
* @param textModified The file contents changed.
* @param propsModified The file's properties changed.
+ * @param nodeInfo Additional information about the modified file.
*/
void modifiedFile(String relativePath,
boolean textModified,
- boolean propsModified);
+ boolean propsModified,
+ Entry nodeInfo);
/**
* A symbolic link was modified.
* @param relativePath The session-relative path of the symlink.
* @param textModified The link target changed.
* @param propsModified The symlink's properties changed.
+ * @param nodeInfo Additional information about the modified symlink.
*/
void modifiedSymlink(String relativePath,
boolean targetModified,
- boolean propsModified);
+ boolean propsModified,
+ Entry nodeInfo);
+
/**
* An entry was deleted.
* @param relativePath The session-relative path of the entry.
*/
void deleted(String relativePath);
+
+
+ /**
+ * Contains additional information related to a modification or
+ * deletion event.
+ */
+ public static class Entry
+ implements Comparable<Entry>, java.io.Serializable
+ {
+ // Update the serialVersionUID when there is a incompatible
+ // change made to this class.
+ private static final long serialVersionUID = 1L;
+
+ private String uuid; // The UUID of the repository
+ private String author; // The author of the last change
+ private long revision; // Committed revision number
+ private long timestamp; // Commit timestamp (milliseconds from epoch)
+
+ public Entry(String uuid, String author, long revision, long timestamp)
+ {
+ this.uuid = uuid;
+ this.author = author;
+ this.revision = revision;
+ this.timestamp = timestamp;
+ }
+
+ /** @return The UUID of the repository that the node belongs to. */
+ public String getUuid() { return uuid; }
+
+ /** @return The author (committer) of the change. */
+ public String getLastAuthor() { return author; }
+
+ /** @return The revision number in with the change was committed. */
+ public long getCommittedRevision() { return revision; }
+
+ /**
+ * @return The timestamp, in milliseconds from the epoch, of
+ * the committed revision.
+ */
+ public long getCommittedTimestamp() { return timestamp; }
+
+ /** Implementation of interface {@link java.lang.Comparable}. */
+ public int compareTo(Entry that)
+ {
+ if (this == that)
+ return 0;
+
+ int cmp = uuid.compareTo(that.uuid);
+ if (cmp == 0) {
+ cmp = author.compareTo(that.author);
+ if (cmp == 0) {
+ cmp = (revision < that.revision ? 1
+ : (revision > that.revision ? -1 : 0));
+ if (cmp == 0)
+ cmp = (timestamp < that.timestamp ? 1
+ : (timestamp > that.timestamp ? -1 : 0));
+ }
+ }
+ return cmp;
+ }
+
+ @Override
+ public boolean equals(Object entry)
+ {
+ if (this == entry)
+ return true;
+ if (!super.equals(entry) || getClass() != entry.getClass())
+ return false;
+
+ final Entry that = (Entry)entry;
+ return (this.uuid == that.uuid
+ && this.author == that.author
+ && this.revision == that.revision
+ && this.timestamp == that.timestamp);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ final int factor = 33;
+ int hash = ((uuid == null) ? 0 : uuid.hashCode());
+ hash = factor * hash + ((author == null) ? 0 : author.hashCode());
+ hash = factor * hash + (int)(revision >> 32) & 0xffffffff;
+ hash = factor * hash + (int)revision & 0xffffffff;
+ hash = factor * hash + (int)(timestamp >> 32) & 0xffffffff;
+ hash = factor * hash + (int)timestamp & 0xffffffff;
+ return hash;
+ }
+ }
}
Modified: subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/StatusEditor.java
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/StatusEditor.java?rev=1499893&r1=1499892&r2=1499893&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/StatusEditor.java (original)
+++ subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/StatusEditor.java Fri Jul 5 00:53:02 2013
@@ -29,7 +29,11 @@ import org.apache.subversion.javahl.call
import org.apache.subversion.javahl.ISVNEditor;
import java.util.Map;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.SimpleTimeZone;
import java.io.InputStream;
+import java.nio.charset.Charset;
/**
* Package-private editor implementation that converts an editor drive
@@ -105,7 +109,8 @@ class StatusEditor implements ISVNEditor
//DEBUG:System.err.println(" [J] StatusEditor.alterDirectory");
checkState();
receiver.modifiedDirectory(relativePath, (children != null),
- props_changed(properties));
+ props_changed(properties),
+ make_entry(properties));
}
public void alterFile(String relativePath,
@@ -118,7 +123,8 @@ class StatusEditor implements ISVNEditor
checkState();
receiver.modifiedFile(relativePath,
(checksum != null && contents != null),
- props_changed(properties));
+ props_changed(properties),
+ make_entry(properties));
}
public void alterSymlink(String relativePath,
@@ -129,7 +135,8 @@ class StatusEditor implements ISVNEditor
//DEBUG:System.err.println(" [J] StatusEditor.alterSymlink");
checkState();
receiver.modifiedSymlink(relativePath, (target != null),
- props_changed(properties));
+ props_changed(properties),
+ make_entry(properties));
}
public void delete(String relativePath, long revision)
@@ -173,6 +180,58 @@ class StatusEditor implements ISVNEditor
}
/*
+ * Construct a RemoteStatus.Entry record from the given properties.
+ */
+ private static final Charset UTF8 = Charset.forName("UTF-8");
+ private static final SimpleTimeZone UTC =
+ new SimpleTimeZone(SimpleTimeZone.UTC_TIME, "UTC");
+ private static final String entryprop_uuid = "svn:entry:uuid";
+ private static final String entryprop_author = "svn:entry:last-author";
+ private static final String entryprop_revision = "svn:entry:committed-rev";
+ private static final String entryprop_timestamp = "svn:entry:committed-date";
+ private final GregorianCalendar entry_calendar =
+ new GregorianCalendar(UTC, Locale.ROOT);
+
+ // FIXME: Room for improvement here. There are likely to be a lot
+ // of duplicate entries and we should be able to avoid parsing the
+ // duplicates all over again. Need a map <raw data> -> Entry to
+ // just look up the entries instead of parsing them.
+ private final RemoteStatus.Entry make_entry(Map<String, byte[]> properties)
+ {
+ final byte[] raw_uuid = properties.get(entryprop_uuid);
+ final byte[] raw_author = properties.get(entryprop_author);
+ final byte[] raw_revision = properties.get(entryprop_revision);
+ final byte[] raw_timestamp = properties.get(entryprop_timestamp);
+
+ long parsed_timestamp = -1;
+ if (raw_timestamp != null)
+ {
+ // Parse: 2013-07-04T23:17:59.128366Z
+ final String isodate = new String(raw_timestamp, UTF8);
+
+ final int year = Integer.valueOf(isodate.substring(0,4), 10);
+ final int month = Integer.valueOf(isodate.substring(5,7), 10);
+ final int day = Integer.valueOf(isodate.substring(8,10), 10);
+ final int hour = Integer.valueOf(isodate.substring(11,13), 10);
+ final int minute = Integer.valueOf(isodate.substring(14,16), 10);
+ final int second = Integer.valueOf(isodate.substring(17,19), 10);
+ final int micro = Integer.valueOf(isodate.substring(20,26), 10);
+ entry_calendar.set(year, month, day, hour, minute, second);
+
+ // Use integer rounding to add milliseconds
+ parsed_timestamp =
+ (1000 * entry_calendar.getTimeInMillis() + micro + 500) / 1000;
+ }
+
+ return new RemoteStatus.Entry(
+ (raw_uuid == null ? null : new String(raw_uuid, UTF8)),
+ (raw_author == null ? null : new String(raw_author, UTF8)),
+ (raw_revision == null ? Revision.SVN_INVALID_REVNUM
+ : Long.valueOf(new String(raw_revision, UTF8), 10)),
+ parsed_timestamp);
+ }
+
+ /*
* Filter entry props from the incoming properties
*/
private static final String wcprop_prefix = "svn:wc:";
Modified: subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java?rev=1499893&r1=1499892&r2=1499893&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java (original)
+++ subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java Fri Jul 5 00:53:02 2013
@@ -794,6 +794,7 @@ public class SVNRemoteTests extends SVNT
public boolean textChanged = false;
public boolean propsChanged = false;
public boolean deleted = false;
+ public Entry info = null;
StatInfo(String relpath, char kind, boolean added)
{
@@ -803,55 +804,106 @@ public class SVNRemoteTests extends SVNT
}
StatInfo(String relpath, char kind,
- boolean textChanged, boolean propsChanged)
+ boolean textChanged, boolean propsChanged,
+ Entry info)
{
this.relpath = relpath;
this.kind = kind;
this.textChanged = textChanged;
this.propsChanged = propsChanged;
+ this.info = info;
}
}
+ private boolean debug;
+
+ public RemoteStatusReceiver()
+ {
+ this.debug = false;
+ }
+
+ public RemoteStatusReceiver(boolean debug)
+ {
+ this.debug = debug;
+ }
+
public ArrayList<StatInfo> status = new ArrayList<StatInfo>();
public void addedDirectory(String relativePath)
{
+ if (debug)
+ System.err.println("RemoteStatus: A (dir) " +
+ relativePath);
status.add(new StatInfo(relativePath, 'D', true));
}
public void addedFile(String relativePath)
{
+ if (debug)
+ System.err.println("RemoteStatus: A (file) "
+ + relativePath);
status.add(new StatInfo(relativePath, 'F', true));
}
public void addedSymlink(String relativePath)
{
+ if (debug)
+ System.err.println("RemoteStatus: A (link) "
+ + relativePath);
status.add(new StatInfo(relativePath, 'L', true));
}
public void modifiedDirectory(String relativePath,
- boolean childrenModified, boolean propsModified)
+ boolean childrenModified,
+ boolean propsModified,
+ Entry nodeInfo)
{
+ if (debug)
+ System.err.println("RemoteStatus: " +
+ (childrenModified ? 'M' : '_') +
+ (propsModified ? 'M' : '_') +
+ " (dir) " + relativePath);
status.add(new StatInfo(relativePath, 'D',
- childrenModified, propsModified));
+ childrenModified, propsModified,
+ nodeInfo));
}
public void modifiedFile(String relativePath,
- boolean textModified, boolean propsModified)
+ boolean textModified,
+ boolean propsModified,
+ Entry nodeInfo)
{
+ if (debug)
+ System.err.println("RemoteStatus: " +
+ (textModified ? 'M' : '_') +
+ (propsModified ? 'M' : '_') +
+ " (file) " + relativePath);
status.add(new StatInfo(relativePath, 'F',
- textModified, propsModified));
+ textModified, propsModified,
+ nodeInfo));
}
public void modifiedSymlink(String relativePath,
- boolean targetModified, boolean propsModified)
+ boolean targetModified,
+ boolean propsModified,
+ Entry nodeInfo)
{
+ if (debug)
+ System.err.println("RemoteStatus: " +
+ (targetModified ? 'M' : '_') +
+ (propsModified ? 'M' : '_') +
+ " (link) " + relativePath);
status.add(new StatInfo(relativePath, 'L',
- targetModified, propsModified));
+ targetModified, propsModified,
+ nodeInfo));
+
}
public void deleted(String relativePath)
{
+ if (debug)
+ System.err.println("RemoteStatus: D "
+ + relativePath);
status.add(new StatInfo(relativePath, ' ', false));
}
}
@@ -900,6 +952,39 @@ public class SVNRemoteTests extends SVNT
assertEquals('F', mod.kind);
assertEquals(false, mod.textChanged);
assertEquals(true, mod.propsChanged);
+ assertEquals(false, mod.deleted);
+ assertEquals(2, mod.info.getCommittedRevision());
+ }
+
+ public void testDeletedStatus() throws Exception
+ {
+ ISVNRemote session = getSession();
+
+ CommitMessageCallback cmcb = new CommitMessageCallback() {
+ public String getLogMessage(Set<CommitItem> x) {
+ return "Delete A/mu";
+ }
+ };
+ HashSet<String> paths = new HashSet<String>(1);
+ paths.add(getTestRepoUrl() + "/A/mu");
+ client.remove(paths, false, false, null, cmcb, null);
+
+ RemoteStatusReceiver receiver = new RemoteStatusReceiver();
+ ISVNReporter rp = session.status(null, Revision.SVN_INVALID_REVNUM,
+ Depth.infinity, receiver);
+ try {
+ rp.setPath("", 1, Depth.infinity, false, null);
+ assertEquals(2, rp.finishReport());
+ } finally {
+ rp.dispose();
+ }
+ assertEquals(3, receiver.status.size());
+ RemoteStatusReceiver.StatInfo mod = receiver.status.get(2);
+ assertEquals("A/mu", mod.relpath);
+ assertEquals(' ', mod.kind);
+ assertEquals(false, mod.textChanged);
+ assertEquals(false, mod.propsChanged);
+ assertEquals(true, mod.deleted);
}
public void testTrivialMergeinfo() throws Exception