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/02 19:45:51 UTC

svn commit: r1499036 - in /subversion/trunk: ./ subversion/bindings/javahl/native/ subversion/bindings/javahl/src/org/apache/subversion/javahl/ subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ subversion/bindings/javahl/src/org/apa...

Author: brane
Date: Tue Jul  2 17:45:50 2013
New Revision: 1499036

URL: http://svn.apache.org/r1499036
Log:
Implement RA status using Ev2 shims in JavaHL.

* build.conf (private-built-includes):
   Add org_apache_subversion_javahl_remote_StateReporter.h.

[in subversion/bindings/javahl/src/org/apache/subversion/javahl]
* callback/RemoteStatus.java: New callback class; provides simpler interface
   to the RA status edit drive.
* ISVNRemote.java (ISVNRemote.status): Replace the statusEditor parameter
   with a RemoteStatus receiver reference.
* remote/RemoteSession.java (ISVNRemote.status): Implement non-native part,
   delegating the entrails to ...
  (RemoteSession.nativeStatus): ... this new private method.

* remote/UpdateReporter.java: Renamed to remote/StateReporter.java.
  remote/StateReporter.java: Rename class, and implement it.

* remote/StatusEditor.java: New; This is the private status editor
   implementation that drives the RemoteStatus callback.

[in subversion/bindings/javahl/native]
* EditorProxy.h, EditorProxy.cpp: New. Proxy for routing Ev2 operations
   to a Java implementation of the Ev2 editor interface (ISVNEditor).
* StateReporter.h, StateReporter.cpp: New. Native implementation of the
   StateReporter java class (i.e., an RA reporter interface).
* org_apache_subversion_javahl_remote_StateReporter.cpp: Native wrappers for same.

* RemoteSession.h (RemoteSession::status): Changed prototype.
  RemoteSession.cpp (RemoteSession::status): Implement.
  (status_unlock_func,
   status_fetch_props_func, status_fetch_base_func,
   status_start_edit_func, status_target_revision_func) New helpers.
* org_apache_subversion_javahl_remote_RemoteSession.cpp
  (Java_org_apache_subversion_javahl_remote_RemoteSession_nativeStatus):
   Update because of changed prototype.

Added:
    subversion/trunk/subversion/bindings/javahl/native/EditorProxy.cpp
    subversion/trunk/subversion/bindings/javahl/native/EditorProxy.h
    subversion/trunk/subversion/bindings/javahl/native/StateReporter.cpp
    subversion/trunk/subversion/bindings/javahl/native/StateReporter.h
    subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_StateReporter.cpp
    subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/RemoteStatus.java
    subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/StateReporter.java
      - copied, changed from r1499024, subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/UpdateReporter.java
    subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/StatusEditor.java
Removed:
    subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/UpdateReporter.java
Modified:
    subversion/trunk/build.conf
    subversion/trunk/subversion/bindings/javahl/native/RemoteSession.cpp
    subversion/trunk/subversion/bindings/javahl/native/RemoteSession.h
    subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteSession.cpp
    subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNRemote.java
    subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteSession.java
    subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java

Modified: subversion/trunk/build.conf
URL: http://svn.apache.org/viewvc/subversion/trunk/build.conf?rev=1499036&r1=1499035&r2=1499036&view=diff
==============================================================================
--- subversion/trunk/build.conf (original)
+++ subversion/trunk/build.conf Tue Jul  2 17:45:50 2013
@@ -70,6 +70,7 @@ private-built-includes =
         subversion/bindings/javahl/include/org_apache_subversion_javahl_remote_RemoteSession.h
         subversion/bindings/javahl/include/org_apache_subversion_javahl_remote_RemoteFactory.h
         subversion/bindings/javahl/include/org_apache_subversion_javahl_remote_CommitEditor.h
+        subversion/bindings/javahl/include/org_apache_subversion_javahl_remote_StateReporter.h
         subversion/bindings/javahl/include/org_apache_subversion_javahl_ConfigImpl_Category.h
 
 test-scripts =

Added: subversion/trunk/subversion/bindings/javahl/native/EditorProxy.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/EditorProxy.cpp?rev=1499036&view=auto
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/EditorProxy.cpp (added)
+++ subversion/trunk/subversion/bindings/javahl/native/EditorProxy.cpp Tue Jul  2 17:45:50 2013
@@ -0,0 +1,527 @@
+/**
+ * @copyright
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file EditorProxy.h
+ * @brief Interface of all editor proxy classes
+ */
+
+#include "JNIUtil.h"
+#include "JNIStackElement.h"
+#include "EditorProxy.h"
+#include "CreateJ.h"
+#include "EnumMapper.h"
+
+#include <apr_pools.h>
+#include "svn_error.h"
+#include "svn_private_config.h"
+
+
+namespace {
+const struct svn_delta__extra_baton null_extra_baton = {0};
+} // anonymous namespace
+
+EditorProxy::EditorProxy(jobject jeditor, apr_pool_t* edit_pool,
+                         const char* repos_root_url, const char* base_relpath,
+                         svn_cancel_func_t cancel_func, void* cancel_baton,
+                         const EditorProxyCallbacks& callbacks)
+  : m_valid(false),
+    m_jeditor(JNIUtil::getEnv()->NewGlobalRef(jeditor)),
+    m_edit_pool(edit_pool),
+    m_repos_root_url(NULL),
+    m_base_relpath(NULL),
+    m_found_paths(false),
+    m_editor(NULL),
+    m_delta_editor(NULL),
+    m_delta_baton(NULL),
+    m_extra_baton(null_extra_baton)
+{
+  //DEBUG:fprintf(stderr, "  (n) EditorProxy::EditorProxy(...)\n");
+
+  static const svn_editor_cb_many_t editor_many_cb = {
+    cb_add_directory, cb_add_file, cb_add_symlink, cb_add_absent,
+    cb_alter_directory, cb_alter_file, cb_alter_symlink,
+    cb_delete, cb_copy, cb_move, cb_rotate,
+    cb_complete, cb_abort
+  };
+
+  SVN::Pool scratchPool(edit_pool);
+  apr_pool_t* scratch_pool = scratchPool.getPool();
+
+  svn_error_t* err = svn_editor_create(&m_editor, this,
+                                       cancel_func, cancel_baton,
+                                       edit_pool, scratch_pool);
+  if (!err)
+    err = svn_editor_setcb_many(m_editor, &editor_many_cb, scratch_pool);
+  if (!err)
+    {
+      m_repos_root_url =
+        static_cast<const char*>(apr_pstrdup(edit_pool, repos_root_url));
+      m_base_relpath =
+        static_cast<const char*>(apr_pstrdup(edit_pool, base_relpath));
+      m_extra_baton.start_edit = callbacks.m_start_edit;
+      m_extra_baton.target_revision = callbacks.m_target_revision;
+      m_extra_baton.baton = callbacks.m_callbacks_baton;
+
+      svn_boolean_t found_paths;
+      err = svn_delta__delta_from_editor(&m_delta_editor,
+                                         &m_delta_baton,
+                                         m_editor,
+                                         callbacks.m_unlock_func,
+                                         callbacks.m_callbacks_baton,
+                                         &found_paths,
+                                         repos_root_url, base_relpath,
+                                         callbacks.m_fetch_props_func,
+                                         callbacks.m_callbacks_baton,
+                                         callbacks.m_fetch_base_func,
+                                         callbacks.m_callbacks_baton,
+                                         &m_extra_baton,
+                                         edit_pool);
+      m_found_paths = found_paths;
+    }
+
+  if (err)
+    JNIUtil::handleSVNError(err);
+  else
+    m_valid = true;
+}
+
+EditorProxy::~EditorProxy()
+{
+  //DEBUG:fprintf(stderr, "  (n) EditorProxy::~EditorProxy()\n");
+
+  if (m_jeditor)
+    JNIUtil::getEnv()->DeleteGlobalRef(m_jeditor);
+}
+
+namespace {
+inline svn_error_t* invalid_editor()
+{
+  return svn_error_create(SVN_ERR_RA_SVN_EDIT_ABORTED, NULL,
+                          _("The editor is not valid"));
+}
+
+svn_error_t*
+get_editor_method(jmethodID& mid, const char* name, const char* sig)
+{
+  if (0 != mid)
+    return SVN_NO_ERROR;        // Already known.
+
+  JNIEnv* env = JNIUtil::getEnv();
+  jclass cls = env->FindClass(JAVA_PACKAGE"/ISVNEditor");
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+  SVN_JNI_CATCH(mid = env->GetMethodID(cls, name, sig),
+                 SVN_ERR_RA_SVN_EDIT_ABORTED);
+  return SVN_NO_ERROR;
+}
+} // anonymous namespace
+
+svn_error_t*
+EditorProxy::cb_add_directory(void *baton,
+                              const char *relpath,
+                              const apr_array_header_t *children,
+                              apr_hash_t *props,
+                              svn_revnum_t replaces_rev,
+                              apr_pool_t *scratch_pool)
+{
+  //DEBUG:fprintf(stderr, "  (n) EditorProxy::cb_add_directory('%s')\n", relpath);
+
+  EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+  if (!ep->m_valid)
+    return invalid_editor();
+
+  static jmethodID mid = 0;
+  SVN_ERR(get_editor_method(mid, "addDirectory",
+                            "(Ljava/lang/String;"
+                            "Ljava/lang/Iterable;"
+                            "Ljava/util/Map;J)V"));
+
+  jstring jrelpath = JNIUtil::makeJString(relpath);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+  jobject jchildren = (!children ? NULL : CreateJ::StringSet(children));
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+  jobject jprops = CreateJ::PropertyMap(props);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+
+  SVN_JNI_CATCH(
+      JNIUtil::getEnv()->CallVoidMethod(ep->m_jeditor, mid,
+                                        jrelpath, jchildren, jprops,
+                                        jlong(replaces_rev)),
+      SVN_ERR_RA_SVN_EDIT_ABORTED);
+  return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_add_file(void *baton,
+                         const char *relpath,
+                         const svn_checksum_t *checksum,
+                         svn_stream_t *contents,
+                         apr_hash_t *props,
+                         svn_revnum_t replaces_rev,
+                         apr_pool_t *scratch_pool)
+{
+  //DEBUG:fprintf(stderr, "  (n) EditorProxy::cb_add_file('%s')\n", relpath);
+
+  EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+  if (!ep || !ep->m_valid)
+    return invalid_editor();
+
+  static jmethodID mid = 0;
+  SVN_ERR(get_editor_method(mid, "addFile",
+                            "(Ljava/lang/String;"
+                            "L"JAVA_PACKAGE"/types/Checksum;"
+                            "Ljava/io/InputStream;"
+                            "Ljava/util/Map;J)V"));
+
+  jstring jrelpath = JNIUtil::makeJString(relpath);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+  jobject jchecksum = CreateJ::Checksum(checksum);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+  jobject jcontents = NULL;     // FIXME: input stream proxy
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+  jobject jprops = CreateJ::PropertyMap(props);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+
+  SVN_JNI_CATCH(
+      JNIUtil::getEnv()->CallVoidMethod(ep->m_jeditor, mid,
+                                        jrelpath, jchecksum, jcontents,
+                                        jprops, jlong(replaces_rev)),
+      SVN_ERR_RA_SVN_EDIT_ABORTED);
+  return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_add_symlink(void *baton,
+                            const char *relpath,
+                            const char *target,
+                            apr_hash_t *props,
+                            svn_revnum_t replaces_rev,
+                            apr_pool_t *scratch_pool)
+{
+  //DEBUG:fprintf(stderr, "  (n) EditorProxy::cb_add_symlink('%s')\n", relpath);
+
+  EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+  if (!ep || !ep->m_valid)
+    return invalid_editor();
+
+  static jmethodID mid = 0;
+  SVN_ERR(get_editor_method(mid, "addSymlink",
+                            "(Ljava/lang/String;"
+                            "Ljava/lang/String;"
+                            "Ljava/util/Map;J)V"));
+
+  jstring jrelpath = JNIUtil::makeJString(relpath);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+  jstring jtarget = JNIUtil::makeJString(target);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+  jobject jprops = CreateJ::PropertyMap(props);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+
+  SVN_JNI_CATCH(
+      JNIUtil::getEnv()->CallVoidMethod(ep->m_jeditor, mid,
+                                        jrelpath, jtarget, jprops,
+                                        jlong(replaces_rev)),
+      SVN_ERR_RA_SVN_EDIT_ABORTED);
+  return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_add_absent(void *baton,
+                           const char *relpath,
+                           svn_node_kind_t kind,
+                           svn_revnum_t replaces_rev,
+                           apr_pool_t *scratch_pool)
+{
+  //DEBUG:fprintf(stderr, "  (n) EditorProxy::cb_add_absent('%s')\n", relpath);
+
+  EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+  if (!ep || !ep->m_valid)
+    return invalid_editor();
+
+  static jmethodID mid = 0;
+  SVN_ERR(get_editor_method(mid, "addAbsent",
+                            "(Ljava/lang/String;"
+                            "L"JAVA_PACKAGE"/types/NodeKind;"
+                            "J)V"));
+
+  jstring jrelpath = JNIUtil::makeJString(relpath);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+  jobject jkind = EnumMapper::mapNodeKind(kind);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+
+  SVN_JNI_CATCH(
+      JNIUtil::getEnv()->CallVoidMethod(ep->m_jeditor, mid,
+                                        jrelpath, jkind,
+                                        jlong(replaces_rev)),
+      SVN_ERR_RA_SVN_EDIT_ABORTED);
+  return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_alter_directory(void *baton,
+                                const char *relpath,
+                                svn_revnum_t revision,
+                                const apr_array_header_t *children,
+                                apr_hash_t *props,
+                                apr_pool_t *scratch_pool)
+{
+  //DEBUG:fprintf(stderr, "  (n) EditorProxy::cb_alter_directory('%s', r%lld)\n",
+  //DEBUG:        relpath, static_cast<long long>(revision));
+
+  EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+  if (!ep || !ep->m_valid)
+    return invalid_editor();
+
+  static jmethodID mid = 0;
+  SVN_ERR(get_editor_method(mid, "alterDirectory",
+                            "(Ljava/lang/String;J"
+                            "Ljava/lang/Iterable;"
+                            "Ljava/util/Map;)V"));
+
+  jstring jrelpath = JNIUtil::makeJString(relpath);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+  jobject jchildren = (!children ? NULL : CreateJ::StringSet(children));
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+  jobject jprops = CreateJ::PropertyMap(props);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+
+  SVN_JNI_CATCH(
+      JNIUtil::getEnv()->CallVoidMethod(ep->m_jeditor, mid,
+                                        jrelpath, jlong(revision),
+                                        jchildren, jprops),
+      SVN_ERR_RA_SVN_EDIT_ABORTED);
+  return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_alter_file(void *baton,
+                           const char *relpath,
+                           svn_revnum_t revision,
+                           const svn_checksum_t *checksum,
+                           svn_stream_t *contents,
+                           apr_hash_t *props,
+                           apr_pool_t *scratch_pool)
+{
+  //DEBUG:fprintf(stderr, "  (n) EditorProxy::cb_alter_file('%s', r%lld)\n",
+  //DEBUG:        relpath, static_cast<long long>(revision));
+
+  EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+  if (!ep || !ep->m_valid)
+    return invalid_editor();
+
+  static jmethodID mid = 0;
+  SVN_ERR(get_editor_method(mid, "alterFile",
+                            "(Ljava/lang/String;J"
+                            "L"JAVA_PACKAGE"/types/Checksum;"
+                            "Ljava/io/InputStream;"
+                            "Ljava/util/Map;)V"));
+
+  jstring jrelpath = JNIUtil::makeJString(relpath);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+  jobject jchecksum = CreateJ::Checksum(checksum);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+  jobject jcontents = NULL;     // FIXME: input stream proxy
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+  jobject jprops = CreateJ::PropertyMap(props);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+
+  SVN_JNI_CATCH(
+      JNIUtil::getEnv()->CallVoidMethod(ep->m_jeditor, mid,
+                                        jrelpath, jlong(revision),
+                                        jchecksum, jcontents, jprops),
+      SVN_ERR_RA_SVN_EDIT_ABORTED);
+  return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_alter_symlink(void *baton,
+                              const char *relpath,
+                              svn_revnum_t revision,
+                              const char *target,
+                              apr_hash_t *props,
+                              apr_pool_t *scratch_pool)
+{
+  //DEBUG:fprintf(stderr, "  (n) EditorProxy::cb_alter_symlink('%s', r%lld)\n",
+  //DEBUG:        relpath, static_cast<long long>(revision));
+
+  EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+  if (!ep || !ep->m_valid)
+    return invalid_editor();
+
+  static jmethodID mid = 0;
+  SVN_ERR(get_editor_method(mid, "alterSymlink",
+                            "(Ljava/lang/String;J"
+                            "Ljava/lang/String;"
+                            "Ljava/util/Map;)V"));
+
+  jstring jrelpath = JNIUtil::makeJString(relpath);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+  jstring jtarget = JNIUtil::makeJString(target);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+  jobject jprops = CreateJ::PropertyMap(props);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+
+  SVN_JNI_CATCH(
+      JNIUtil::getEnv()->CallVoidMethod(ep->m_jeditor, mid,
+                                        jrelpath, jlong(revision),
+                                        jtarget, jprops),
+      SVN_ERR_RA_SVN_EDIT_ABORTED);
+  return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_delete(void *baton,
+                       const char *relpath,
+                       svn_revnum_t revision,
+                       apr_pool_t *scratch_pool)
+{
+  //DEBUG:fprintf(stderr, "  (n) EditorProxy::cb_delete('%s', r%lld)\n",
+  //DEBUG:        relpath, static_cast<long long>(revision));
+
+  EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+  if (!ep || !ep->m_valid)
+    return invalid_editor();
+
+  static jmethodID mid = 0;
+  SVN_ERR(get_editor_method(mid, "delete",
+                            "(Ljava/lang/String;J)V"));
+
+  jstring jrelpath = JNIUtil::makeJString(relpath);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+
+  SVN_JNI_CATCH(
+      JNIUtil::getEnv()->CallVoidMethod(ep->m_jeditor, mid, jrelpath),
+      SVN_ERR_RA_SVN_EDIT_ABORTED);
+  return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_copy(void *baton,
+                     const char *src_relpath,
+                     svn_revnum_t src_revision,
+                     const char *dst_relpath,
+                     svn_revnum_t replaces_rev,
+                     apr_pool_t *scratch_pool)
+{
+  //DEBUG:fprintf(stderr, "  (n) EditorProxy::cb_copy('%s', r%lld, '%s')\n",
+  //DEBUG:        src_relpath, static_cast<long long>(src_revision), dst_relpath);
+
+  EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+  if (!ep || !ep->m_valid)
+    return invalid_editor();
+
+  static jmethodID mid = 0;
+  SVN_ERR(get_editor_method(mid, "copy",
+                            "(Ljava/lang/String;J"
+                            "Ljava/lang/String;J)V"));
+
+  jstring jsrc_relpath = JNIUtil::makeJString(src_relpath);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+  jstring jdst_relpath = JNIUtil::makeJString(dst_relpath);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+
+  SVN_JNI_CATCH(
+      JNIUtil::getEnv()->CallVoidMethod(ep->m_jeditor, mid,
+                                        jsrc_relpath, jlong(src_revision),
+                                        jdst_relpath, jlong(replaces_rev)),
+      SVN_ERR_RA_SVN_EDIT_ABORTED);
+  return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_move(void *baton,
+                     const char *src_relpath,
+                     svn_revnum_t src_revision,
+                     const char *dst_relpath,
+                     svn_revnum_t replaces_rev,
+                     apr_pool_t *scratch_pool)
+{
+  //DEBUG:fprintf(stderr, "  (n) EditorProxy::cb_move('%s', r%lld, '%s')\n",
+  //DEBUG:        src_relpath, static_cast<long long>(src_revision), dst_relpath);
+
+  EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+  if (!ep || !ep->m_valid)
+    return invalid_editor();
+
+  static jmethodID mid = 0;
+  SVN_ERR(get_editor_method(mid, "move",
+                            "(Ljava/lang/String;J"
+                            "Ljava/lang/String;J)V"));
+
+  jstring jsrc_relpath = JNIUtil::makeJString(src_relpath);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+  jstring jdst_relpath = JNIUtil::makeJString(dst_relpath);
+  SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+
+  SVN_JNI_CATCH(
+      JNIUtil::getEnv()->CallVoidMethod(ep->m_jeditor, mid,
+                                        jsrc_relpath, jlong(src_revision),
+                                        jdst_relpath, jlong(replaces_rev)),
+      SVN_ERR_RA_SVN_EDIT_ABORTED);
+  return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_rotate(void*,
+                       const apr_array_header_t*,
+                       const apr_array_header_t*,
+                       apr_pool_t*)
+{
+  return svn_error_create(APR_ENOTIMPL, NULL, "EditorProxy::cb_rotate");
+}
+
+svn_error_t*
+EditorProxy::cb_complete(void *baton, apr_pool_t *scratch_pool)
+{
+  //DEBUG:fprintf(stderr, "  (n) EditorProxy::cb_complete()\n");
+
+  EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+  if (!ep || !ep->m_valid)
+    return invalid_editor();
+  ep->m_valid = false;
+
+  static jmethodID mid = 0;
+  SVN_ERR(get_editor_method(mid, "complete", "()V"));
+
+  SVN_JNI_CATCH(
+      JNIUtil::getEnv()->CallVoidMethod(ep->m_jeditor, mid),
+      SVN_ERR_RA_SVN_EDIT_ABORTED);
+  return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_abort(void *baton, apr_pool_t *scratch_pool)
+{
+  //DEBUG:fprintf(stderr, "  (n) EditorProxy::cb_abort()\n");
+
+  EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+  if (!ep || !ep->m_valid)
+    return invalid_editor();
+  ep->m_valid = false;
+
+  static jmethodID mid = 0;
+  SVN_ERR(get_editor_method(mid, "abort", "()V"));
+
+  SVN_JNI_CATCH(
+      JNIUtil::getEnv()->CallVoidMethod(ep->m_jeditor, mid),
+      SVN_ERR_RA_SVN_EDIT_ABORTED);
+  return SVN_NO_ERROR;
+}

Added: subversion/trunk/subversion/bindings/javahl/native/EditorProxy.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/EditorProxy.h?rev=1499036&view=auto
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/EditorProxy.h (added)
+++ subversion/trunk/subversion/bindings/javahl/native/EditorProxy.h Tue Jul  2 17:45:50 2013
@@ -0,0 +1,160 @@
+/**
+ * @copyright
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file EditorProxy.h
+ * @brief Interface of all editor proxy classes
+ */
+
+#ifndef JAVAHL_EDITOR_PROXY_H
+#define JAVAHL_EDITOR_PROXY_H
+
+#include "svn_delta.h"
+#include "private/svn_editor.h"
+#include "private/svn_delta_private.h"
+
+/**
+ * These callbacks are needed by the delta-to-Ev2 shims.
+ */
+struct EditorProxyCallbacks
+{
+  svn_delta__unlock_func_t m_unlock_func;
+  svn_delta_fetch_props_func_t m_fetch_props_func;
+  svn_delta_fetch_base_func_t m_fetch_base_func;
+  svn_delta__start_edit_func_t m_start_edit;
+  svn_delta__target_revision_func_t m_target_revision;
+  void* m_callbacks_baton;
+};
+
+/**
+ * This is a proxy object that translates Ev2 operations (possibly
+ * implemented through shims) into calls to a Java editor
+ * implementation.
+ */
+class EditorProxy
+{
+public:
+  EditorProxy(jobject jeditor, apr_pool_t* edit_pool,
+              const char* repos_root_url, const char* base_relpath,
+              svn_cancel_func_t cancel_func, void* cancel_baton,
+              const EditorProxyCallbacks& callbacks);
+  ~EditorProxy();
+
+  const svn_delta_editor_t* delta_editor() const
+    {
+      return m_delta_editor;
+    }
+
+  void* delta_baton() const
+    {
+      return m_delta_baton;
+    }
+
+private:
+  EditorProxy(const EditorProxy&); // noncopyable
+
+  static svn_error_t* cb_add_directory(void *baton,
+                                       const char *relpath,
+                                       const apr_array_header_t *children,
+                                       apr_hash_t *props,
+                                       svn_revnum_t replaces_rev,
+                                       apr_pool_t *scratch_pool);
+  static svn_error_t* cb_add_file(void *baton,
+                                  const char *relpath,
+                                  const svn_checksum_t *checksum,
+                                  svn_stream_t *contents,
+                                  apr_hash_t *props,
+                                  svn_revnum_t replaces_rev,
+                                  apr_pool_t *scratch_pool);
+  static svn_error_t* cb_add_symlink(void *baton,
+                                     const char *relpath,
+                                     const char *target,
+                                     apr_hash_t *props,
+                                     svn_revnum_t replaces_rev,
+                                     apr_pool_t *scratch_pool);
+  static svn_error_t* cb_add_absent(void *baton,
+                                    const char *relpath,
+                                    svn_node_kind_t kind,
+                                    svn_revnum_t replaces_rev,
+                                    apr_pool_t *scratch_pool);
+  static svn_error_t* cb_alter_directory(void *baton,
+                                         const char *relpath,
+                                         svn_revnum_t revision,
+                                         const apr_array_header_t *children,
+                                         apr_hash_t *props,
+                                         apr_pool_t *scratch_pool);
+  static svn_error_t* cb_alter_file(void *baton,
+                                    const char *relpath,
+                                    svn_revnum_t revision,
+                                    const svn_checksum_t *checksum,
+                                    svn_stream_t *contents,
+                                    apr_hash_t *props,
+                                    apr_pool_t *scratch_pool);
+  static svn_error_t* cb_alter_symlink(void *baton,
+                                       const char *relpath,
+                                       svn_revnum_t revision,
+                                       const char *target,
+                                       apr_hash_t *props,
+                                       apr_pool_t *scratch_pool);
+  static svn_error_t* cb_delete(void *baton,
+                                const char *relpath,
+                                svn_revnum_t revision,
+                                apr_pool_t *scratch_pool);
+  static svn_error_t* cb_copy(void *baton,
+                              const char *src_relpath,
+                              svn_revnum_t src_revision,
+                              const char *dst_relpath,
+                              svn_revnum_t replaces_rev,
+                              apr_pool_t *scratch_pool);
+  static svn_error_t* cb_move(void *baton,
+                              const char *src_relpath,
+                              svn_revnum_t src_revision,
+                              const char *dst_relpath,
+                              svn_revnum_t replaces_rev,
+                              apr_pool_t *scratch_pool);
+  static svn_error_t* cb_rotate(void *baton,
+                                const apr_array_header_t *relpaths,
+                                const apr_array_header_t *revisions,
+                                apr_pool_t *scratch_pool);
+  static svn_error_t* cb_complete(void *baton,
+                                  apr_pool_t *scratch_pool);
+  static svn_error_t* cb_abort(void *baton,
+                               apr_pool_t *scratch_pool);
+
+private:
+  bool m_valid;
+  jobject m_jeditor;            ///< Reference to Java editor implementation
+  apr_pool_t* m_edit_pool;
+
+  const char* m_repos_root_url; ///< The root of the repository
+  const char* m_base_relpath;   ///< The root of the session within the repo
+  bool m_found_paths;           ///< Returned paths are absolute
+  
+  svn_editor_t* m_editor;
+  const svn_delta_editor_t* m_delta_editor;
+  void* m_delta_baton;
+
+  // Initialized from the EditorProxyCallbacks struct.
+  struct svn_delta__extra_baton m_extra_baton;
+};
+
+
+#endif // JAVAHL_EDITOR_PROXY_H

Modified: subversion/trunk/subversion/bindings/javahl/native/RemoteSession.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/RemoteSession.cpp?rev=1499036&r1=1499035&r2=1499036&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/RemoteSession.cpp (original)
+++ subversion/trunk/subversion/bindings/javahl/native/RemoteSession.cpp Tue Jul  2 17:45:50 2013
@@ -25,6 +25,7 @@
  */
 
 #include <cstring>
+#include <memory>
 #include <set>
 
 #include "JNIByteArray.h"
@@ -44,6 +45,8 @@
 #include "Prompter.h"
 #include "Revision.h"
 #include "RemoteSession.h"
+#include "EditorProxy.h"
+#include "StateReporter.h"
 
 #include <apr_strings.h>
 #include "svn_private_config.h"
@@ -639,12 +642,109 @@ RemoteSession::getDirectory(jlong jrevis
 // TODO: update
 // TODO: switch
 
-jobject
-RemoteSession::status(jstring jstatus_target,
+namespace {
+svn_error_t*
+status_unlock_func(void* baton, const char* path, apr_pool_t* scratch_pool)
+{
+  //DEBUG:fprintf(stderr, "  (n) status_unlock_func('%s')\n", path);
+  return SVN_NO_ERROR;
+}
+
+svn_error_t*
+status_fetch_props_func(apr_hash_t **props, void *baton,
+                        const char *path, svn_revnum_t base_revision,
+                        apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+  //DEBUG:fprintf(stderr, "  (n) status_fetch_props_func('%s', r%lld)\n",
+  //DEBUG:        path, static_cast<long long>(base_revision));
+  *props = apr_hash_make(scratch_pool);
+  return SVN_NO_ERROR;
+}
+
+svn_error_t*
+status_fetch_base_func(const char **filename, void *baton,
+                       const char *path, svn_revnum_t base_revision,
+                       apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+  //DEBUG:fprintf(stderr, "  (n) status_fetch_base_func('%s', r%lld)\n",
+  //DEBUG:        path, static_cast<long long>(base_revision));
+  *filename = NULL;
+  return SVN_NO_ERROR;
+}
+
+svn_error_t*
+status_start_edit_func(void* baton, svn_revnum_t start_revision)
+{
+  //DEBUG:fprintf(stderr, "  (n) status_start_edit_func(r%lld)\n",
+  //DEBUG:        static_cast<long long>(start_revision));
+  return SVN_NO_ERROR;
+}
+
+svn_error_t*
+status_target_revision_func(void* baton, svn_revnum_t target_revision,
+                            apr_pool_t* scratch_pool)
+{
+  //DEBUG:fprintf(stderr, "  (n) status_target_revision_func(r%lld)\n",
+  //DEBUG:        static_cast<long long>(target_revision));
+  return SVN_NO_ERROR;
+}
+
+const EditorProxyCallbacks status_editor_callbacks = {
+  status_unlock_func,
+  status_fetch_props_func,
+  status_fetch_base_func,
+  status_start_edit_func,
+  status_target_revision_func,
+  NULL
+};
+} // anonymous namespace
+
+void
+RemoteSession::status(jobject jthis, jstring jstatus_target,
                       jlong jrevision, jobject jdepth,
-                      jobject jstatus_editor)
+                      jobject jstatus_editor, jobject jreporter)
 {
-  return NULL;
+  StateReporter *rp = StateReporter::getCppObject(jreporter);
+  CPPADDR_NULL_PTR(rp,);
+
+  SVN::Pool scratchPool(rp->get_report_pool());
+  Relpath status_target(jstatus_target, scratchPool);
+  if (JNIUtil::isExceptionThrown())
+    return;
+
+  apr_pool_t* scratch_pool = scratchPool.getPool();
+  const char* repos_root_url;
+  SVN_JNI_ERR(svn_ra_get_repos_root2(m_session, &repos_root_url,
+                                     scratch_pool),);
+  const char* session_root_url;
+  SVN_JNI_ERR(svn_ra_get_session_url(m_session, &session_root_url,
+                                     scratch_pool),);
+  const char* base_relpath;
+  SVN_JNI_ERR(svn_ra_get_path_relative_to_root(m_session, &base_relpath,
+                                               session_root_url,
+                                               scratch_pool),);
+
+
+  apr_pool_t* report_pool = rp->get_report_pool();
+  std::auto_ptr<EditorProxy> editor(
+      new EditorProxy(jstatus_editor, report_pool,
+                      repos_root_url, base_relpath,
+                      m_context->checkCancel, m_context,
+                      status_editor_callbacks));
+  if (JNIUtil::isExceptionThrown())
+    return;
+
+  const svn_ra_reporter3_t* raw_reporter;
+  void* report_baton;
+  SVN_JNI_ERR(svn_ra_do_status2(m_session,
+                                &raw_reporter, &report_baton,
+                                status_target.c_str(),
+                                svn_revnum_t(jrevision),
+                                EnumMapper::toDepth(jdepth),
+                                editor->delta_editor(),
+                                editor->delta_baton(),
+                                report_pool),);
+  rp->set_reporter_data(raw_reporter, report_baton, editor.release());
 }
 
 // TODO: diff

Modified: subversion/trunk/subversion/bindings/javahl/native/RemoteSession.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/RemoteSession.h?rev=1499036&r1=1499035&r2=1499036&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/RemoteSession.h (original)
+++ subversion/trunk/subversion/bindings/javahl/native/RemoteSession.h Tue Jul  2 17:45:50 2013
@@ -82,9 +82,9 @@ class RemoteSession : public SVNBase
     // TODO: getMergeinfo
     // TODO: update
     // TODO: switch
-    jobject status(jstring jstatus_target,
-                   jlong jrevision, jobject jdepth,
-                   jobject jstatus_editor);
+    void status(jobject jthis, jstring jstatus_target,
+                jlong jrevision, jobject jdepth,
+                jobject jstatus_editor, jobject jreporter);
     // TODO: diff
     void getLog(jobject jpaths, jlong jstartrev, jlong jendrev, jint jlimit,
                 jboolean jstrict_node_history, jboolean jdiscover_changed_paths,

Added: subversion/trunk/subversion/bindings/javahl/native/StateReporter.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/StateReporter.cpp?rev=1499036&view=auto
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/StateReporter.cpp (added)
+++ subversion/trunk/subversion/bindings/javahl/native/StateReporter.cpp Tue Jul  2 17:45:50 2013
@@ -0,0 +1,186 @@
+/**
+ * @copyright
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file StateReporter.cpp
+ * @brief Implementation of the class StateReporter
+ */
+
+#include <jni.h>
+
+#include "JNIUtil.h"
+#include "JNIStringHolder.h"
+#include "StateReporter.h"
+#include "EnumMapper.h"
+#include "Path.h"
+
+#include "svn_private_config.h"
+
+StateReporter::StateReporter()
+  : m_valid(false),
+    m_raw_reporter(NULL),
+    m_report_baton(NULL),
+    m_editor(NULL)
+{}
+
+StateReporter::~StateReporter()
+{
+  delete m_editor;
+}
+
+StateReporter*
+StateReporter::getCppObject(jobject jthis)
+{
+  static jfieldID fid = 0;
+  jlong cppAddr = SVNBase::findCppAddrForJObject(jthis, &fid,
+      JAVA_PACKAGE"/remote/StateReporter");
+  return (cppAddr == 0 ? NULL : reinterpret_cast<StateReporter*>(cppAddr));
+}
+
+void
+StateReporter::dispose(jobject jthis)
+{
+  //DEBUG:fprintf(stderr, "  (n) StateReporter::dispose()\n");
+
+  if (m_valid)
+    abortReport();
+
+  static jfieldID fid = 0;
+  SVNBase::dispose(jthis, &fid, JAVA_PACKAGE"/remote/StateReporter");
+}
+
+namespace {
+void throw_reporter_inactive()
+{
+  JNIUtil::raiseThrowable("java/lang/IllegalStateException",
+                          _("The reporter is not active"));
+}
+} // anonymous namespace
+
+void
+StateReporter::setPath(jstring jpath, jlong jrevision, jobject jdepth,
+                       jboolean jstart_empty, jstring jlock_token)
+{
+  //DEBUG:fprintf(stderr, "  (n) StateReporter::setPath()\n");
+
+  if (!m_valid) { throw_reporter_inactive(); return; }
+  
+  JNIStringHolder lock_token(jlock_token);
+  if (JNIUtil::isJavaExceptionThrown())
+    return;
+
+  SVN::Pool subPool(pool);
+  Relpath path(jpath, subPool);
+  if (JNIUtil::isJavaExceptionThrown())
+    return;
+  svn_depth_t depth = EnumMapper::toDepth(jdepth);
+  if (JNIUtil::isJavaExceptionThrown())
+    return;
+
+  SVN_JNI_ERR(m_raw_reporter->set_path(m_report_baton, path.c_str(),
+                                       svn_revnum_t(jrevision), depth,
+                                       bool(jstart_empty), lock_token.c_str(),
+                                       subPool.getPool()),);
+}
+
+void
+StateReporter::deletePath(jstring jpath)
+{
+  //DEBUG:fprintf(stderr, "  (n) StateReporter::deletePath()\n");
+
+  if (!m_valid) { throw_reporter_inactive(); return; }
+
+  SVN::Pool subPool(pool);
+  Relpath path(jpath, subPool);
+  if (JNIUtil::isJavaExceptionThrown())
+    return;
+
+  SVN_JNI_ERR(m_raw_reporter->delete_path(m_report_baton, path.c_str(),
+                                          subPool.getPool()),);
+}
+
+void
+StateReporter::linkPath(jstring jurl, jstring jpath,
+                        jlong jrevision, jobject jdepth,
+                        jboolean jstart_empty, jstring jlock_token)
+{
+  //DEBUG:fprintf(stderr, "  (n) StateReporter::linkPath()\n");
+
+  if (!m_valid) { throw_reporter_inactive(); return; }
+
+  JNIStringHolder lock_token(jlock_token);
+  if (JNIUtil::isJavaExceptionThrown())
+    return;
+
+  SVN::Pool subPool(pool);
+  Relpath path(jpath, subPool);
+  if (JNIUtil::isJavaExceptionThrown())
+    return;
+  URL url(jurl, subPool);
+  if (JNIUtil::isJavaExceptionThrown())
+    return;
+  svn_depth_t depth = EnumMapper::toDepth(jdepth);
+  if (JNIUtil::isJavaExceptionThrown())
+    return;
+
+  SVN_JNI_ERR(m_raw_reporter->link_path(m_report_baton, path.c_str(),
+                                        url.c_str(), svn_revnum_t(jrevision),
+                                        depth, bool(jstart_empty),
+                                        lock_token.c_str(),
+                                        subPool.getPool()),);
+}
+
+void
+StateReporter::finishReport()
+{
+  //DEBUG:fprintf(stderr, "  (n) StateReporter::finishReport()\n");
+
+  if (!m_valid) { throw_reporter_inactive(); return; }
+
+  SVN::Pool subPool(pool);
+  SVN_JNI_ERR(m_raw_reporter->finish_report(m_report_baton,
+                                            subPool.getPool()),);
+}
+
+void
+StateReporter::abortReport()
+{
+  //DEBUG:fprintf(stderr, "  (n) StateReporter::abortReport()\n");
+
+  if (!m_valid) { throw_reporter_inactive(); return; }
+
+  SVN::Pool subPool(pool);
+  SVN_JNI_ERR(m_raw_reporter->abort_report(m_report_baton,
+                                           subPool.getPool()),);
+}
+
+void
+StateReporter::set_reporter_data(const svn_ra_reporter3_t* raw_reporter,
+                                   void* report_baton,
+                                   EditorProxy* editor)
+{
+  //DEBUG:fprintf(stderr, "  (n) StateReporter::set_reporter_data()\n");
+
+  m_editor = editor;
+  m_raw_reporter = raw_reporter;
+  m_report_baton = report_baton;
+  m_valid = true;
+}

Added: subversion/trunk/subversion/bindings/javahl/native/StateReporter.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/StateReporter.h?rev=1499036&view=auto
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/StateReporter.h (added)
+++ subversion/trunk/subversion/bindings/javahl/native/StateReporter.h Tue Jul  2 17:45:50 2013
@@ -0,0 +1,73 @@
+/**
+ * @copyright
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file UpdateReporter.h
+ * @brief Interface of the class UpdateReporter
+ */
+
+#ifndef JAVAHL_UPDATE_REPORTER_H
+#define JAVAHL_UPDATE_REPORTER_H
+
+#include <jni.h>
+
+#include "svn_ra.h"
+#include "SVNBase.h"
+#include "EditorProxy.h"
+
+class RemoteSession;
+
+/*
+ * This class wraps the update/status/switch/diff reporter in svn_ra.h
+ */
+class StateReporter : public SVNBase
+{
+public:
+  StateReporter();
+  virtual ~StateReporter();
+
+  static StateReporter* getCppObject(jobject jreporter);
+
+  virtual void dispose(jobject jthis);
+
+  void setPath(jstring jpath, jlong jrevision, jobject jdepth,
+               jboolean jstart_empty, jstring jlock_token);
+  void deletePath(jstring jpath);
+  void linkPath(jstring jurl, jstring jpath, jlong jrevision, jobject jdepth,
+                jboolean jstart_empty, jstring jlock_token);
+  void finishReport();
+  void abortReport();
+
+private:
+
+  bool m_valid;
+  const svn_ra_reporter3_t* m_raw_reporter;
+  void* m_report_baton;
+  EditorProxy* m_editor;
+
+  friend class RemoteSession;
+  apr_pool_t* get_report_pool() const { return pool.getPool(); }
+  void set_reporter_data(const svn_ra_reporter3_t* raw_reporter,
+                         void* report_baton,
+                         EditorProxy* editor);
+};
+
+#endif // JAVAHL_UPDATE_REPORTER_H

Modified: subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteSession.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteSession.cpp?rev=1499036&r1=1499035&r2=1499036&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteSession.cpp (original)
+++ subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteSession.cpp Tue Jul  2 17:45:50 2013
@@ -217,16 +217,17 @@ Java_org_apache_subversion_javahl_remote
 // TODO: update
 // TODO: switch
 
-JNIEXPORT jobject JNICALL
-Java_org_apache_subversion_javahl_remote_RemoteSession_status(
-    JNIEnv *env, jobject jthis, jstring jstatus_target,
-    jlong jrevision, jobject jdepth, jobject jstatus_editor)
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_nativeStatus(
+    JNIEnv *env, jobject jthis, jstring jstatus_target, jlong jrevision,
+    jobject jdepth, jobject jstatus_editor, jobject jreporter)
 {
-  JNIEntry(SVNReposAccess, doStatus);
+  JNIEntry(SVNReposAccess, nativeStatus);
   RemoteSession *ras = RemoteSession::getCppObject(jthis);
-  CPPADDR_NULL_PTR(ras, NULL);
+  CPPADDR_NULL_PTR(ras,);
 
-  return ras->status(jstatus_target, jrevision, jdepth, jstatus_editor);
+  ras->status(jthis, jstatus_target, jrevision, jdepth,
+              jstatus_editor, jreporter);
 }
 
 // TODO: diff

Added: subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_StateReporter.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_StateReporter.cpp?rev=1499036&view=auto
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_StateReporter.cpp (added)
+++ subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_StateReporter.cpp Tue Jul  2 17:45:50 2013
@@ -0,0 +1,117 @@
+/**
+ * @copyright
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file org_apache_subversion_javahl_remote_StateReporter.cpp
+ * @brief Implementation of the native methods in the Java class StateReporter
+ */
+
+#include "../include/org_apache_subversion_javahl_remote_StateReporter.h"
+
+#include <jni.h>
+#include "JNIStackElement.h"
+#include "JNIUtil.h"
+#include "StateReporter.h"
+
+#include "svn_private_config.h"
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_StateReporter_finalize(
+    JNIEnv *env, jobject jthis)
+{
+  JNIEntry(StateReporter, finalize);
+  StateReporter *reporter = StateReporter::getCppObject(jthis);
+  if (reporter != NULL)
+    reporter->finalize();
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_StateReporter_nativeDispose(
+    JNIEnv* env, jobject jthis)
+{
+  JNIEntry(StateReporter, nativeCreateInstance);
+  StateReporter* reporter = StateReporter::getCppObject(jthis);
+  CPPADDR_NULL_PTR(reporter,);
+  reporter->dispose(jthis);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_StateReporter_setPath(
+    JNIEnv* env, jobject jthis,
+    jstring jpath, jlong jrevision, jobject jdepth,
+    jboolean jstart_empty, jstring jlock_token)
+{
+  JNIEntry(StateReporter, setPath);
+  StateReporter* reporter = StateReporter::getCppObject(jthis);
+  CPPADDR_NULL_PTR(reporter,);
+  reporter->setPath(jpath, jrevision, jdepth, jstart_empty, jlock_token);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_StateReporter_deletePath(
+    JNIEnv* env, jobject jthis, jstring jpath)
+{
+  JNIEntry(StateReporter, deletePath);
+  StateReporter* reporter = StateReporter::getCppObject(jthis);
+  CPPADDR_NULL_PTR(reporter,);
+  reporter->deletePath(jpath);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_StateReporter_linkPath(
+    JNIEnv* env, jobject jthis,
+    jstring jurl, jstring jpath, jlong jrevision, jobject jdepth,
+    jboolean jstart_empty, jstring jlock_token)
+{
+  JNIEntry(StateReporter, linkPath);
+  StateReporter* reporter = StateReporter::getCppObject(jthis);
+  CPPADDR_NULL_PTR(reporter,);
+  reporter->linkPath(jurl, jpath, jrevision, jdepth, jstart_empty, jlock_token);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_StateReporter_finishReport(
+    JNIEnv* env, jobject jthis)
+{
+  JNIEntry(StateReporter, finishReport);
+  StateReporter* reporter = StateReporter::getCppObject(jthis);
+  CPPADDR_NULL_PTR(reporter,);
+  reporter->finishReport();
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_StateReporter_abortReport(
+    JNIEnv* env, jobject jthis)
+{
+  JNIEntry(StateReporter, abortReport);
+  StateReporter* reporter = StateReporter::getCppObject(jthis);
+  CPPADDR_NULL_PTR(reporter,);
+  reporter->abortReport();
+}
+
+JNIEXPORT jlong JNICALL
+Java_org_apache_subversion_javahl_remote_StateReporter_nativeCreateInstance(
+    JNIEnv* env, jclass clazz)
+{
+  jobject jthis = NULL;
+  JNIEntry(StateReporter, nativeCreateInstance);
+  return reinterpret_cast<jlong>(new StateReporter);
+}

Modified: subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNRemote.java
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNRemote.java?rev=1499036&r1=1499035&r2=1499036&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNRemote.java (original)
+++ subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNRemote.java Tue Jul  2 17:45:50 2013
@@ -289,8 +289,8 @@ public interface ISVNRemote
      * or the HEAD revision if <code>revision</code> is
      * {@link org.apache.subversion.javahl.types.Revision#SVN_INVALID_REVNUM}.
      * <p>
-     * The client begins by providing a <code>statusEditor</code> to
-     * the remote session; this editor must contain knowledge of where
+     * The client begins by providing a <code>receiver</code> to
+     * the remote session; this object must contain knowledge of where
      * the change will begin in the working copy.
      * <p>
      * In return, the client receives an {@link ISVNReporter}
@@ -298,14 +298,12 @@ public interface ISVNRemote
      * calls to its methods.
      * <p>
      * When finished, the client calls {@link ISVNReporter#finishReport}.
-     * This results in a complete drive of <code>statusEditor</code>,
-     * ending with {@link ISVNEditor#complete()}, to report,
-     * essentially, what would be modified in the working copy were
-     * the client to perform an update.  <code>statusTarget</code> is
-     * an optional single path component that restricts the scope of
-     * the status report to an entry in the directory represented by
-     * the session's URL, or empty if the entire directory is meant to
-     * be examined.
+     * This results in <code>receiver</code> being called once for
+     * every path in the working copy that is different from the
+     * repository. <code>statusTarget</code> is an optional single
+     * path component that restricts the scope of the status report to
+     * an entry in the directory represented by the session's URL, or
+     * empty if the entire directory is meant to be examined.
      * <p>
      * Get status as deeply as <code>depth</code> indicates.  If
      * <code>depth</code> is
@@ -317,8 +315,8 @@ public interface ISVNRemote
      * <p>
      * The caller may not perform any operations using this session
      * before finishing the report, and may not perform any operations
-     * using this session from within the editing operations of
-     * <code>statusEditor</code>.
+     * using this session from within the implementation of
+     * <code>receiver</code>.
      * <p>
      * <b>Note:</b> The reporter provided by this function does
      * <em>not</em> supply copy-from information to the editor
@@ -332,7 +330,7 @@ public interface ISVNRemote
      */
     ISVNReporter status(String statusTarget,
                         long revision, Depth depth,
-                        ISVNEditor statusEditor)
+                        RemoteStatus receiver)
             throws ClientException;
 
     // TODO: diff

Added: 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=1499036&view=auto
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/RemoteStatus.java (added)
+++ subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/RemoteStatus.java Tue Jul  2 17:45:50 2013
@@ -0,0 +1,92 @@
+/**
+ * @copyright
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+package org.apache.subversion.javahl.callback;
+
+import org.apache.subversion.javahl.ISVNRemote;
+
+/**
+ * Called for each affected element in a remote status driave.
+ * <p>
+ * <b>Note:</b> All paths sent to the callback methods are relative to
+ * the {@link ISVNRemtoe} session's URL.
+ * @see ISVNRemote#status
+ * @since 1.9
+ */
+public interface RemoteStatus
+{
+    /**
+     * A directory was added.
+     * @param relativePath The session-relative path of the new directory.
+     */
+    void addedDirectory(String relativePath);
+
+    /**
+     * A file was added.
+     * @param relativePath The session-relative path of the new file.
+     */
+    void addedFile(String relativePath);
+
+    /**
+     * A symbolic link was added.
+     * @param relativePath The session-relative path of the new symbolic link.
+     */
+    void addedSymlink(String relativePath);
+
+    /**
+     * A directory was modified.
+     * @param relativePath The session-relative path of the directory.
+     * @param revision The revision in which it was last modified.
+     * @param childrenModified The directory contents changed.
+     * @param propsModified The directory's properties changed.
+     */
+    void modifiedDirectory(String relativePath, long revision,
+                           boolean childrenModified, boolean propsModified);
+
+    /**
+     * A file was modified.
+     * @param relativePath The session-relative path of the directory.
+     * @param revision The revision in which it was last modified.
+     * @param textModified The file contents changed.
+     * @param propsModified The file's properties changed.
+     */
+    void modifiedFile(String relativePath, long revision,
+                      boolean textModified, boolean propsModified);
+
+    /**
+     * A symbolic link was modified.
+     * @param relativePath The session-relative path of the symlink.
+     * @param revision The revision in which it was last modified.
+     * @param textModified The link target changed.
+     * @param propsModified The symlink's properties changed.
+     */
+    void modifiedSymlink(String relativePath, long revision,
+                         boolean targetModified, boolean propsModified);
+
+    /**
+     * An entry was deleted.
+     * @param relativePath The session-relative path of the entry.
+     * @param revision The revision in which it was deleted.
+     */
+    void deleted(String relativePath, long revision);
+}

Modified: subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteSession.java
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteSession.java?rev=1499036&r1=1499035&r2=1499036&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteSession.java (original)
+++ subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteSession.java Tue Jul  2 17:45:50 2013
@@ -163,10 +163,24 @@ public class RemoteSession extends JNIOb
     // TODO: update
     // TODO: switch
 
-    public native ISVNReporter status(String statusTarget,
-                                      long revision, Depth depth,
-                                      ISVNEditor statusEditor)
-            throws ClientException;
+    public ISVNReporter status(String statusTarget,
+                               long revision, Depth depth,
+                               RemoteStatus receiver)
+            throws ClientException
+    {
+        check_inactive(editorReference, reporterReference);
+        StateReporter rp = StateReporter.createInstance(this);
+
+        // At this point, the reporter is not active/valid.
+        StatusEditor editor = new StatusEditor(receiver);
+        nativeStatus(statusTarget, revision, depth, editor, rp);
+        // Now it should be valid.
+
+        if (reporterReference != null)
+            reporterReference.clear();
+        reporterReference = new WeakReference<ISVNReporter>(rp);
+        return rp;
+    }
 
     // TODO: diff
 
@@ -227,12 +241,16 @@ public class RemoteSession extends JNIOb
                                       OutputStream contents,
                                       Map<String, byte[]> properties)
             throws ClientException;
-
     private native long nativeGetDirectory(long revision, String path,
                                            int direntFields,
                                            Map<String, DirEntry> dirents,
                                            Map<String, byte[]> properties)
             throws ClientException;
+    private native void nativeStatus(String statusTarget,
+                                     long revision, Depth depth,
+                                     ISVNEditor statusEditor,
+                                     ISVNReporter reporter)
+            throws ClientException;
     private native boolean nativeHasCapability(String capability)
             throws ClientException;
 

Copied: subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/StateReporter.java (from r1499024, subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/UpdateReporter.java)
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/StateReporter.java?p2=subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/StateReporter.java&p1=subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/UpdateReporter.java&r1=1499024&r2=1499036&rev=1499036&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/UpdateReporter.java (original)
+++ subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/StateReporter.java Tue Jul  2 17:45:50 2013
@@ -34,7 +34,7 @@ import org.apache.subversion.javahl.Clie
  * Implementation of ISVNReporter.
  * @since 1.9
  */
-public class UpdateReporter extends JNIObject implements ISVNReporter
+public class StateReporter extends JNIObject implements ISVNReporter
 {
     public void dispose()
     {
@@ -42,51 +42,52 @@ public class UpdateReporter extends JNIO
         nativeDispose();
     }
 
-    public void setPath(String path,
-                        long revision,
-                        Depth depth,
-                        boolean startEmpty,
-                        String lockToken)
-            throws ClientException
-    {
-        throw new RuntimeException("Not implemented: setPath");
-    }
+    public native void setPath(String path,
+                               long revision,
+                               Depth depth,
+                               boolean startEmpty,
+                               String lockToken)
+        throws ClientException;
+
+    public native void deletePath(String path) throws ClientException;
+
+    public native void linkPath(String url,
+                                String path,
+                                long revision,
+                                Depth depth,
+                                boolean startEmpty,
+                                String lockToken)
+        throws ClientException;
 
-    public void deletePath(String path) throws ClientException
-    {
-        throw new RuntimeException("Not implemented: deletePath");
-    }
+    public native void finishReport() throws ClientException;
 
-    public void linkPath(String url,
-                         String path,
-                         long revision,
-                         Depth depth,
-                         boolean startEmpty,
-                         String lockToken)
-            throws ClientException
-    {
-        throw new RuntimeException("Not implemented: linkPath");
-    }
+    public native void abortReport() throws ClientException;
 
-    public void finishReport() throws ClientException
+    /**
+     * This factory method called from RemoteSession.status and friends.
+     */
+    static final
+        StateReporter createInstance(RemoteSession session)
+            throws ClientException
     {
-        throw new RuntimeException("Not implemented: finishReport");
+        long cppAddr = nativeCreateInstance();
+        return new StateReporter(cppAddr, session);
     }
 
-    public void abortReport() throws ClientException
-    {
-        throw new RuntimeException("Not implemented: abortReport");
-    }
+    @Override
+    public native void finalize() throws Throwable;
 
     /*
      * Wrapped private native implementation declarations.
      */
-    private /*TODO:native*/ void nativeDispose() {;}
+    private native void nativeDispose();
+    private static final native long nativeCreateInstance()
+        throws ClientException;
 
     /**
-     * This constructor is called from the factory to get an instance.
+     * This constructor is called from the factory method.
      */
-    protected UpdateReporter(long cppAddr, RemoteSession session)
+    protected StateReporter(long cppAddr, RemoteSession session)
     {
         super(cppAddr);
         this.session = session;

Added: 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=1499036&view=auto
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/StatusEditor.java (added)
+++ subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/StatusEditor.java Tue Jul  2 17:45:50 2013
@@ -0,0 +1,189 @@
+/**
+ * @copyright
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+package org.apache.subversion.javahl.remote;
+
+import org.apache.subversion.javahl.types.*;
+import org.apache.subversion.javahl.callback.*;
+
+import org.apache.subversion.javahl.ISVNEditor;
+
+import java.util.Map;
+import java.io.InputStream;
+
+/**
+ * Package-private editor implementation that converts an editor drive
+ * to {@link RemoteStatus} callbacks.
+ * @since 1.9
+ */
+class StatusEditor implements ISVNEditor
+{
+    StatusEditor(RemoteStatus receiver)
+    {
+        this.receiver = receiver;
+    }
+    protected RemoteStatus receiver = null;
+
+    protected void checkState()
+    {
+        if (receiver == null)
+            throw new IllegalStateException("Status editor is not active");
+    }
+
+    public void dispose()
+    {
+        //DEBUG:System.err.println("  [J] StatusEditor.dispose");
+        if (this.receiver != null)
+            abort();
+    }
+
+    public void addDirectory(String relativePath,
+                             Iterable<String> children,
+                             Map<String, byte[]> properties,
+                             long replacesRevision)
+    {
+        //DEBUG:System.err.println("  [J] StatusEditor.addDirectory");
+        checkState();
+        receiver.addedDirectory(relativePath);
+    }
+
+    public void addFile(String relativePath,
+                        Checksum checksum,
+                        InputStream contents,
+                        Map<String, byte[]> properties,
+                        long replacesRevision)
+    {
+        //DEBUG:System.err.println("  [J] StatusEditor.addFile");
+        checkState();
+        receiver.addedFile(relativePath);
+    }
+
+    public void addSymlink(String relativePath,
+                           String target,
+                           Map<String, byte[]> properties,
+                           long replacesRevision)
+    {
+        //DEBUG:System.err.println("  [J] StatusEditor.addSymlink");
+        checkState();
+        receiver.addedSymlink(relativePath);
+    }
+
+    public void addAbsent(String relativePath,
+                          NodeKind kind,
+                          long replacesRevision)
+    {
+        //DEBUG:System.err.println("  [J] StatusEditor.addAbsent");
+        checkState();
+        throw new RuntimeException("Not implemented: StatusEditor.addAbsent");
+    }
+
+    public void alterDirectory(String relativePath,
+                               long revision,
+                               Iterable<String> children,
+                               Map<String, byte[]> properties)
+    {
+        //DEBUG:System.err.println("  [J] StatusEditor.alterDirectory");
+        checkState();
+        receiver.modifiedDirectory(relativePath, revision, (children != null),
+                                   props_changed(properties));
+    }
+
+    public void alterFile(String relativePath,
+                          long revision,
+                          Checksum checksum,
+                          InputStream contents,
+                          Map<String, byte[]> properties)
+    {
+        //DEBUG:System.err.println("  [J] StatusEditor.alterFile");
+        checkState();
+        receiver.modifiedFile(relativePath, revision,
+                              (checksum != null && contents != null),
+                              props_changed(properties));
+    }
+
+    public void alterSymlink(String relativePath,
+                             long revision,
+                             String target,
+                             Map<String, byte[]> properties)
+    {
+        //DEBUG:System.err.println("  [J] StatusEditor.alterSymlink");
+        checkState();
+        receiver.modifiedSymlink(relativePath, revision, (target != null),
+                                 props_changed(properties));
+    }
+
+    public void delete(String relativePath, long revision)
+    {
+        //DEBUG:System.err.println("  [J] StatusEditor.delete");
+        checkState();
+        receiver.deleted(relativePath, revision);
+    }
+
+    public void copy(String sourceRelativePath,
+                     long sourceRevision,
+                     String destinationRelativePath,
+                     long replacesRevision)
+    {
+        //DEBUG:System.err.println("  [J] StatusEditor.copy");
+        checkState();
+        throw new RuntimeException("Not implemented: StatusEditor.copy");
+    }
+
+    public void move(String sourceRelativePath,
+                     long sourceRevision,
+                     String destinationRelativePath,
+                     long replacesRevision)
+    {
+        //DEBUG:System.err.println("  [J] StatusEditor.move");
+        checkState();
+        throw new RuntimeException("Not implemented: StatusEditor.move");
+    }
+
+    public void complete()
+    {
+        //DEBUG:System.err.println("  [J] StatusEditor.complete");
+        abort();
+    }
+
+    public void abort()
+    {
+        //DEBUG:System.err.println("  [J] StatusEditor.abort");
+        checkState();
+        receiver = null;
+    }
+
+    /*
+     * Filter entry props from the incoming properties
+     */
+    private static final String wcprop_prefix = "svn:wc:";
+    private static final String entryprop_prefix = "svn:entry:";
+    private static final boolean props_changed(Map<String, byte[]> properties)
+    {
+        if (properties != null)
+            for (String name : properties.keySet())
+                if (!name.startsWith(wcprop_prefix)
+                    && !name.startsWith(entryprop_prefix))
+                    return true;
+        return false;
+    }
+}

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=1499036&r1=1499035&r2=1499036&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 Tue Jul  2 17:45:50 2013
@@ -659,7 +659,7 @@ public class SVNRemoteTests extends SVNT
             try {
                 exmsg = "";
                 cc.editor.addSymlink("", "", props, 1);
-            } catch (IllegalStateException ex) {
+            } catch (RuntimeException ex) {
                 exmsg = ex.getMessage();
             }
             assertEquals("Not implemented: CommitEditor.addSymlink", exmsg);
@@ -667,7 +667,7 @@ public class SVNRemoteTests extends SVNT
             try {
                 exmsg = "";
                 cc.editor.alterSymlink("", 1, "", null);
-            } catch (IllegalStateException ex) {
+            } catch (RuntimeException ex) {
                 exmsg = ex.getMessage();
             }
             assertEquals("Not implemented: CommitEditor.alterSymlink", exmsg);
@@ -675,10 +675,10 @@ public class SVNRemoteTests extends SVNT
             // try {
             //     exmsg = "";
             //     cc.editor.rotate(rotation);
-            // } catch (IllegalStateException ex) {
+            // } catch (RuntimeException ex) {
             //     exmsg = ex.getMessage();
             // }
-            assertEquals("Not implemented: CommitEditor.rotate", exmsg);
+            // assertEquals("Not implemented: CommitEditor.rotate", exmsg);
         } finally {
             cc.editor.dispose();
         }
@@ -783,4 +783,124 @@ public class SVNRemoteTests extends SVNT
                                         handler);
         session.getLatestRevision(); // Make sure the configuration gets loaded
     }
+
+    private static class RemoteStatusReceiver implements RemoteStatus
+    {
+        static class StatInfo
+        {
+            public String relpath = null;
+            public char kind = ' '; // F, D, L
+            public boolean textChanged = false;
+            public boolean propsChanged = false;
+            public boolean deleted = false;
+            public long revision = -1;
+
+            StatInfo(String relpath, char kind, boolean added)
+            {
+                this.relpath = relpath;
+                this.kind = kind;
+                this.deleted = !added;
+            }
+
+            StatInfo(String relpath, char kind, long revision,
+                     boolean textChanged, boolean propsChanged)
+            {
+                this.relpath = relpath;
+                this.kind = kind;
+                this.textChanged = textChanged;
+                this.propsChanged = propsChanged;
+                this.revision = revision;
+            }
+        }
+
+        public ArrayList<StatInfo> status = new ArrayList<StatInfo>();
+
+        public void addedDirectory(String relativePath)
+        {
+            status.add(new StatInfo(relativePath, 'D', true));
+        }
+
+        public void addedFile(String relativePath)
+        {
+            status.add(new StatInfo(relativePath, 'F', true));
+        }
+
+        public void addedSymlink(String relativePath)
+        {
+            status.add(new StatInfo(relativePath, 'L', true));
+        }
+
+        public void modifiedDirectory(String relativePath, long revision,
+                                      boolean childrenModified, boolean propsModified)
+        {
+            status.add(new StatInfo(relativePath, 'D', revision,
+                                    childrenModified, propsModified));
+        }
+
+        public void modifiedFile(String relativePath, long revision,
+                                 boolean textModified, boolean propsModified)
+        {
+            status.add(new StatInfo(relativePath, 'F', revision,
+                                    textModified, propsModified));
+        }
+
+        public void modifiedSymlink(String relativePath, long revision,
+                                    boolean targetModified, boolean propsModified)
+        {
+            status.add(new StatInfo(relativePath, 'L', revision,
+                                    targetModified, propsModified));
+        }
+
+        public void deleted(String relativePath, long revision)
+        {
+            status.add(new StatInfo(relativePath, ' ', false));
+        }
+    }
+
+    public void testSimpleStatus() throws Exception
+    {
+        ISVNRemote session = getSession();
+
+        RemoteStatusReceiver receiver = new RemoteStatusReceiver();
+        ISVNReporter rp = session.status(null, Revision.SVN_INVALID_REVNUM,
+                                         Depth.infinity, receiver);
+        try {
+            rp.setPath("", 0, Depth.infinity, true, null);
+            rp.finishReport();
+        } finally {
+            rp.dispose();
+        }
+        assertEquals(21, receiver.status.size());
+    }
+
+    public void testPropchangeStatus() throws Exception
+    {
+        ISVNRemote session = getSession();
+
+        CommitMessageCallback cmcb = new CommitMessageCallback() {
+                public String getLogMessage(Set<CommitItem> x) {
+                    return "Property change on A/D/gamma";
+                }
+            };
+        client.propertySetRemote(getTestRepoUrl() + "/A/D/gamma",
+                                 1L, "foo", "bar".getBytes(), cmcb,
+                                 false, null, 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);
+            rp.finishReport();
+        } finally {
+            rp.dispose();
+        }
+        assertEquals(4, receiver.status.size());
+        RemoteStatusReceiver.StatInfo mod = receiver.status.get(3);
+        assertEquals("A/D/gamma", mod.relpath);
+        assertEquals('F', mod.kind);
+        assertEquals(false, mod.textChanged);
+        assertEquals(true, mod.propsChanged);
+        assertEquals(1, mod.revision);
+    }
 }