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