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/06/27 14:33:22 UTC

svn commit: r1497326 - in /subversion/branches/javahl-1.8-extensions: ./ subversion/bindings/javahl/ subversion/bindings/javahl/native/ subversion/bindings/javahl/src/org/apache/subversion/javahl/ subversion/bindings/javahl/src/org/apache/subversion/ja...

Author: brane
Date: Thu Jun 27 12:33:21 2013
New Revision: 1497326

URL: http://svn.apache.org/r1497326
Log:
On the javahl-1.8-extensions branch: Sync JavaHL with trunk up to r1497316.

Added:
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/CommitEditor.cpp
      - copied, changed from r1497316, subversion/trunk/subversion/bindings/javahl/native/CommitEditor.cpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/CommitEditor.h
      - copied unchanged from r1497316, subversion/trunk/subversion/bindings/javahl/native/CommitEditor.h
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/Iterator.cpp
      - copied unchanged from r1497316, subversion/trunk/subversion/bindings/javahl/native/Iterator.cpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/Iterator.h
      - copied unchanged from r1497316, subversion/trunk/subversion/bindings/javahl/native/Iterator.h
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/LockTokenTable.cpp
      - copied unchanged from r1497316, subversion/trunk/subversion/bindings/javahl/native/LockTokenTable.cpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/LockTokenTable.h
      - copied unchanged from r1497316, subversion/trunk/subversion/bindings/javahl/native/LockTokenTable.h
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_CommitEditor.cpp
      - copied unchanged from r1497316, subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_CommitEditor.cpp
Modified:
    subversion/branches/javahl-1.8-extensions/build.conf
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/   (props changed)
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/CommitCallback.cpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/CommitCallback.h
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/EnumMapper.cpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/EnumMapper.h
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/JNIUtil.cpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/JNIUtil.h
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/OperationContext.cpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/Path.cpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/Path.h
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/RemoteSession.cpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/RemoteSession.h
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/SVNClient.cpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteSession.cpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/ClientException.java
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNEditor.java
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNRemote.java
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/CommitEditor.java
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteSession.java
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java

Modified: subversion/branches/javahl-1.8-extensions/build.conf
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/build.conf?rev=1497326&r1=1497325&r2=1497326&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/build.conf (original)
+++ subversion/branches/javahl-1.8-extensions/build.conf Thu Jun 27 12:33:21 2013
@@ -66,7 +66,7 @@ private-built-includes =
         subversion/bindings/javahl/include/org_apache_subversion_javahl_callback_UserPasswordCallback.h
         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
 
 test-scripts =
         subversion/tests/cmdline/*_tests.py

Propchange: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/
------------------------------------------------------------------------------
  Merged /subversion/trunk/subversion/bindings/javahl:r1496335-1497316

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/CommitCallback.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/CommitCallback.cpp?rev=1497326&r1=1497325&r2=1497326&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/CommitCallback.cpp (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/CommitCallback.cpp Thu Jun 27 12:33:21 2013
@@ -37,9 +37,8 @@
  * @param jcallback the Java callback object.
  */
 CommitCallback::CommitCallback(jobject jcallback)
-{
-  m_callback = jcallback;
-}
+  : m_callback(jcallback)
+{}
 
 /**
  * Destroy a CommitCallback object
@@ -102,3 +101,14 @@ CommitCallback::commitInfo(const svn_com
   env->PopLocalFrame(NULL);
   return SVN_NO_ERROR;
 }
+
+
+PersistentCommitCallback::PersistentCommitCallback(jobject jcallback)
+  : CommitCallback(JNIUtil::getEnv()->NewGlobalRef(jcallback))
+{}
+
+PersistentCommitCallback::~PersistentCommitCallback()
+{
+  if (m_callback)
+    JNIUtil::getEnv()->DeleteGlobalRef(m_callback);
+}

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/CommitCallback.h
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/CommitCallback.h?rev=1497326&r1=1497325&r2=1497326&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/CommitCallback.h (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/CommitCallback.h Thu Jun 27 12:33:21 2013
@@ -47,11 +47,26 @@ class CommitCallback
   svn_error_t *commitInfo(const svn_commit_info_t *commit_info,
                           apr_pool_t *pool);
 
- private:
   /**
    * This a local reference to the Java object.
    */
   jobject m_callback;
 };
 
+/**
+ * Like CommitCallback, but maintains a reference to the Java object
+ * across JNI calls.
+ */
+class PersistentCommitCallback : protected CommitCallback
+{
+ public:
+  PersistentCommitCallback(jobject jcallback);
+  ~PersistentCommitCallback();
+  static svn_error_t *callback(const svn_commit_info_t *commit_info,
+                               void *baton, apr_pool_t *pool)
+    {
+      return CommitCallback::callback(commit_info, baton, pool);
+    }
+};
+
 #endif  // COMMITCALLBACK_H

Copied: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/CommitEditor.cpp (from r1497316, subversion/trunk/subversion/bindings/javahl/native/CommitEditor.cpp)
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/CommitEditor.cpp?p2=subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/CommitEditor.cpp&p1=subversion/trunk/subversion/bindings/javahl/native/CommitEditor.cpp&r1=1497316&r2=1497326&rev=1497326&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/CommitEditor.cpp (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/CommitEditor.cpp Thu Jun 27 12:33:21 2013
@@ -427,9 +427,9 @@ void CommitEditor::alterFile(jstring jre
     return;
   SVN_JNI_ERR(svn_editor_alter_file(
                   m_editor, relpath.c_str(), svn_revnum_t(jrevision),
+                  properties.hash(subPool, true),
                   (jcontents ? &checksum : NULL),
-                  (jcontents ? contents.getStream(subPool) : NULL),
-                  properties.hash(subPool, true)),);
+                  (jcontents ? contents.getStream(subPool) : NULL)),);
 }
 
 void CommitEditor::alterSymlink(jstring jrelpath, jlong jrevision,

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/EnumMapper.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/EnumMapper.cpp?rev=1497326&r1=1497325&r2=1497326&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/EnumMapper.cpp (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/EnumMapper.cpp Thu Jun 27 12:33:21 2013
@@ -191,6 +191,18 @@ int EnumMapper::toLogLevel(jobject jLogL
   return getOrdinal(JAVA_PACKAGE"/SVNClient$ClientLogLevel", jLogLevel);
 }
 
+svn_node_kind_t EnumMapper::toNodeKind(jobject jNodeKind)
+{
+  return svn_node_kind_t(
+      getOrdinal(JAVA_PACKAGE"/types/NodeKind", jNodeKind));
+}
+
+svn_checksum_kind_t EnumMapper::toChecksumKind(jobject jChecksumKind)
+{
+  return svn_checksum_kind_t(
+      getOrdinal(JAVA_PACKAGE"/types/Checksum$Kind", jChecksumKind));
+}
+
 svn_depth_t EnumMapper::toDepth(jobject jdepth)
 {
   // The offset for depths is -2

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/EnumMapper.h
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/EnumMapper.h?rev=1497326&r1=1497325&r2=1497326&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/EnumMapper.h (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/EnumMapper.h Thu Jun 27 12:33:21 2013
@@ -48,6 +48,8 @@ class EnumMapper
   static svn_wc_conflict_choice_t toConflictChoice(jobject jchoice);
   static int toMergeinfoLogKind(jobject jLogKind);
   static int toLogLevel(jobject jLogLevel);
+  static svn_node_kind_t toNodeKind(jobject jNodeKind);
+  static svn_checksum_kind_t toChecksumKind(jobject jChecksumKind);
 
   /* Converting from C enum's */
   static jint mapCommitMessageStateFlags(apr_byte_t flags);

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/JNIUtil.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/JNIUtil.cpp?rev=1497326&r1=1497325&r2=1497326&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/JNIUtil.cpp (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/JNIUtil.cpp Thu Jun 27 12:33:21 2013
@@ -418,10 +418,66 @@ JNIUtil::putErrorsInTrace(svn_error_t *e
   env->DeleteLocalRef(jfileName);
 }
 
+namespace {
+jobject construct_Jmessage_stack(
+    const JNIUtil::error_message_stack_t& message_stack)
+{
+  JNIEnv *env = JNIUtil::getEnv();
+  env->PushLocalFrame(LOCAL_FRAME_SIZE);
+  if (JNIUtil::isJavaExceptionThrown())
+    return NULL;
+
+  jclass list_clazz = env->FindClass("java/util/ArrayList");
+  if (JNIUtil::isJavaExceptionThrown())
+    POP_AND_RETURN_NULL;
+  jmethodID mid = env->GetMethodID(list_clazz, "<init>", "(I)V");
+  if (JNIUtil::isJavaExceptionThrown())
+    POP_AND_RETURN_NULL;
+  jmethodID add_mid = env->GetMethodID(list_clazz, "add",
+                                       "(Ljava/lang/Object;)Z");
+  if (JNIUtil::isJavaExceptionThrown())
+    POP_AND_RETURN_NULL;
+  jobject jlist = env->NewObject(list_clazz, mid, jint(message_stack.size()));
+  if (JNIUtil::isJavaExceptionThrown())
+    POP_AND_RETURN_NULL;
+
+  jclass clazz = env->FindClass(JAVA_PACKAGE"/ClientException$ErrorMessage");
+  if (JNIUtil::isJavaExceptionThrown())
+    POP_AND_RETURN_NULL;
+  mid = env->GetMethodID(clazz, "<init>",
+                         "(ILjava/lang/String;Z)V");
+  if (JNIUtil::isJavaExceptionThrown())
+    POP_AND_RETURN_NULL;
+
+  for (JNIUtil::error_message_stack_t::const_iterator
+         it = message_stack.begin();
+       it != message_stack.end(); ++it)
+    {
+      jobject jmessage = JNIUtil::makeJString(it->m_message.c_str());
+      if (JNIUtil::isJavaExceptionThrown())
+        POP_AND_RETURN_NULL;
+      jobject jitem = env->NewObject(clazz, mid,
+                                     jint(it->m_code), jmessage,
+                                     jboolean(it->m_generic));
+      if (JNIUtil::isJavaExceptionThrown())
+        POP_AND_RETURN_NULL;
+      env->CallBooleanMethod(jlist, add_mid, jitem);
+      if (JNIUtil::isJavaExceptionThrown())
+        POP_AND_RETURN_NULL;
+
+      env->DeleteLocalRef(jmessage);
+      env->DeleteLocalRef(jitem);
+    }
+  return env->PopLocalFrame(jlist);
+}
+} // anonymous namespace
+
 void JNIUtil::handleSVNError(svn_error_t *err)
 {
   std::string msg;
-  assembleErrorMessage(svn_error_purge_tracing(err), 0, APR_SUCCESS, msg);
+  error_message_stack_t message_stack;
+  assembleErrorMessage(svn_error_purge_tracing(err),
+                       0, APR_SUCCESS, msg, &message_stack);
   const char *source = NULL;
 #ifdef SVN_DEBUG
 #ifndef SVN_ERR__TRACING
@@ -472,12 +528,18 @@ void JNIUtil::handleSVNError(svn_error_t
   if (isJavaExceptionThrown())
     POP_AND_RETURN_NOTHING();
 
+  jobject jmessageStack = construct_Jmessage_stack(message_stack);
+  if (isJavaExceptionThrown())
+    POP_AND_RETURN_NOTHING();
+
   jmethodID mid = env->GetMethodID(clazz, "<init>",
-                                   "(Ljava/lang/String;Ljava/lang/String;I)V");
+                                   "(Ljava/lang/String;"
+                                   "Ljava/lang/String;I"
+                                   "Ljava/util/List;)V");
   if (isJavaExceptionThrown())
     POP_AND_RETURN_NOTHING();
   jobject nativeException = env->NewObject(clazz, mid, jmessage, jsource,
-                                           static_cast<jint>(err->apr_err));
+                                           jint(err->apr_err), jmessageStack);
   if (isJavaExceptionThrown())
     POP_AND_RETURN_NOTHING();
 
@@ -875,13 +937,15 @@ jbyteArray JNIUtil::makeJByteArray(const
  * @param parent_apr_err    the apr of the previous level, used for formating
  * @param buffer            the buffer where the formated error message will
  *                          be stored
+ * @param message_stack     an array of error codes and messages
  */
 void JNIUtil::assembleErrorMessage(svn_error_t *err, int depth,
                                    apr_status_t parent_apr_err,
-                                   std::string &buffer)
+                                   std::string &buffer,
+                                   error_message_stack_t* message_stack)
 {
   // buffer for a single error message
-  char errbuf[256];
+  char errbuf[1024];
 
   /* Pretty-print the error */
   /* Note: we can also log errors here someday. */
@@ -890,35 +954,43 @@ void JNIUtil::assembleErrorMessage(svn_e
    * the same as before. */
   if (depth == 0 || err->apr_err != parent_apr_err)
     {
+      const char *message;
       /* Is this a Subversion-specific error code? */
       if ((err->apr_err > APR_OS_START_USEERR)
           && (err->apr_err <= APR_OS_START_CANONERR))
-        buffer.append(svn_strerror(err->apr_err, errbuf, sizeof(errbuf)));
+        message = svn_strerror(err->apr_err, errbuf, sizeof(errbuf));
       /* Otherwise, this must be an APR error code. */
       else
         {
           /* Messages coming from apr_strerror are in the native
              encoding, it's a good idea to convert them to UTF-8. */
-          const char* utf8_message;
           apr_strerror(err->apr_err, errbuf, sizeof(errbuf));
-          svn_error_t* utf8_err = svn_utf_cstring_to_utf8(
-              &utf8_message, errbuf, err->pool);
+          svn_error_t* utf8_err =
+            svn_utf_cstring_to_utf8(&message, errbuf, err->pool);
           if (utf8_err)
             {
               /* Use fuzzy transliteration instead. */
               svn_error_clear(utf8_err);
-              utf8_message = svn_utf_cstring_from_utf8_fuzzy(errbuf, err->pool);
+              message = svn_utf_cstring_from_utf8_fuzzy(errbuf, err->pool);
             }
-          buffer.append(utf8_message);
         }
+
+      if (message_stack)
+        message_stack->push_back(
+            message_stack_item(err->apr_err, message, true));
+      buffer.append(message);
       buffer.append("\n");
     }
   if (err->message)
-    buffer.append(_("svn: ")).append(err->message).append("\n");
+    {
+      if (message_stack)
+        message_stack->push_back(
+            message_stack_item(err->apr_err, err->message));
+      buffer.append(_("svn: ")).append(err->message).append("\n");
+    }
 
   if (err->child)
     assembleErrorMessage(err->child, depth + 1, err->apr_err, buffer);
-
 }
 
 /**

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/JNIUtil.h
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/JNIUtil.h?rev=1497326&r1=1497325&r2=1497326&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/JNIUtil.h (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/JNIUtil.h Thu Jun 27 12:33:21 2013
@@ -37,6 +37,7 @@ class SVNBase;
 #include <fstream>
 #include <apr_time.h>
 #include <string>
+#include <vector>
 struct svn_error_t;
 
 #define JAVA_PACKAGE "org/apache/subversion/javahl"
@@ -141,10 +142,26 @@ class JNIUtil
   enum { formatBufferSize = 2048 };
   enum { noLog, errorLog, exceptionLog, entryLog } LogLevel;
 
+  struct message_stack_item
+  {
+    apr_status_t m_code;
+    std::string m_message;
+    bool m_generic;
+
+    message_stack_item(apr_status_t code, const char* message,
+                       bool generic = false)
+      : m_code(code),
+        m_message(message),
+        m_generic(generic)
+      {}
+  };
+  typedef std::vector<message_stack_item> error_message_stack_t;
+
  private:
   static void assembleErrorMessage(svn_error_t *err, int depth,
                                    apr_status_t parent_apr_err,
-                                   std::string &buffer);
+                                   std::string &buffer,
+                                   error_message_stack_t* message_stack = NULL);
   static void putErrorsInTrace(svn_error_t *err,
                                std::vector<jobject> &stackTrace);
   /**

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/OperationContext.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/OperationContext.cpp?rev=1497326&r1=1497325&r2=1497326&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/OperationContext.cpp (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/OperationContext.cpp Thu Jun 27 12:33:21 2013
@@ -215,10 +215,7 @@ OperationContext::getAuthBaton(SVN::Pool
 
 jobject OperationContext::getSelf() const
 {
-  jobject jctx = JNIUtil::getEnv()->NewGlobalRef(m_jctx);
-  if (JNIUtil::isJavaExceptionThrown())
-    return NULL;
-  return jctx;
+  return m_jctx;
 }
 
 void

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/Path.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/Path.cpp?rev=1497326&r1=1497325&r2=1497326&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/Path.cpp (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/Path.cpp Thu Jun 27 12:33:21 2013
@@ -27,18 +27,24 @@
 #include <jni.h>
 #include "Path.h"
 #include "svn_path.h"
+#include "svn_dirent_uri.h"
 #include "JNIUtil.h"
+#include "JNIStringHolder.h"
 #include "Pool.h"
+#include "svn_private_config.h"
 
 /**
  * Constructor
  *
- * @see Path::Path(const std::string &)
+ * @see PathBase::PathBase(const std::string &)
  * @param path Path string
  */
-Path::Path(const char *pi_path, SVN::Pool &in_pool)
+PathBase::PathBase(const char *pi_path,
+                   svn_error_t* initfunc(const char*&, SVN::Pool&),
+                   SVN::Pool &in_pool)
+  : m_error_occurred(NULL)
 {
-  init(pi_path, in_pool);
+  init(pi_path, initfunc, in_pool);
 }
 
 /**
@@ -48,19 +54,26 @@ Path::Path(const char *pi_path, SVN::Poo
  *
  * @param path Path string
  */
-Path::Path(const std::string &pi_path, SVN::Pool &in_pool)
+PathBase::PathBase(const std::string &pi_path,
+                   svn_error_t* initfunc(const char*&, SVN::Pool&),
+                   SVN::Pool &in_pool)
+  : m_error_occurred(NULL)
 {
-  init(pi_path.c_str(), in_pool);
+  init(pi_path.c_str(), initfunc, in_pool);
 }
 
 /**
- * Copy constructor
- *
- * @param path Path to be copied
+ * Constructor from a Java string.
  */
-Path::Path(const Path &pi_path, SVN::Pool &in_pool)
+PathBase::PathBase(jstring jpath,
+                   svn_error_t* initfunc(const char*&, SVN::Pool&),
+                   SVN::Pool &in_pool)
+  : m_error_occurred(NULL)
 {
-  init(pi_path.c_str(), in_pool);
+  JNIStringHolder path(jpath);
+  if (JNIUtil::isJavaExceptionThrown())
+    return;
+  init(path, initfunc, in_pool);
 }
 
 /**
@@ -69,17 +82,13 @@ Path::Path(const Path &pi_path, SVN::Poo
  * @param path Path string
  */
 void
-Path::init(const char *pi_path, SVN::Pool &in_pool)
+PathBase::init(const char *pi_path,
+               svn_error_t* initfunc(const char*&, SVN::Pool&),
+               SVN::Pool &in_pool)
 {
-  if (*pi_path == 0)
-    {
-      m_error_occurred = NULL;
-      m_path = "";
-    }
-  else
+  if (pi_path && *pi_path)
     {
-      m_error_occurred = JNIUtil::preprocessPath(pi_path, in_pool.getPool());
-
+      m_error_occurred = initfunc(pi_path, in_pool);
       m_path = pi_path;
     }
 }
@@ -88,7 +97,7 @@ Path::init(const char *pi_path, SVN::Poo
  * @return Path string
  */
 const std::string &
-Path::path() const
+PathBase::path() const
 {
   return m_path;
 }
@@ -97,7 +106,7 @@ Path::path() const
  * @return Path string as a C string
  */
 const char *
-Path::c_str() const
+PathBase::c_str() const
 {
   return m_path.c_str();
 }
@@ -105,8 +114,8 @@ Path::c_str() const
 /**
  * Assignment operator
  */
-Path&
-Path::operator=(const Path &pi_path)
+PathBase&
+PathBase::operator=(const PathBase &pi_path)
 {
   m_error_occurred = NULL;
   m_path = pi_path.m_path;
@@ -114,12 +123,12 @@ Path::operator=(const Path &pi_path)
   return *this;
 }
 
-  svn_error_t *Path::error_occurred() const
+svn_error_t *PathBase::error_occurred() const
 {
   return m_error_occurred;
 }
 
-jboolean Path::isValid(const char *p)
+jboolean PathBase::isValid(const char *p)
 {
   if (p == NULL)
     return JNI_FALSE;
@@ -136,3 +145,25 @@ jboolean Path::isValid(const char *p)
       return JNI_FALSE;
     }
 }
+
+svn_error_t*
+Path::initfunc(const char*& path, SVN::Pool& pool)
+{
+  return JNIUtil::preprocessPath(path, pool.getPool());
+}
+
+svn_error_t*
+URL::initfunc(const char*& path, SVN::Pool& pool)
+{
+  if (svn_path_is_url(path))
+    return JNIUtil::preprocessPath(path, pool.getPool());
+  return svn_error_createf(SVN_ERR_BAD_URL, NULL,
+                           _("Not an URL: %s"), path);
+}
+
+svn_error_t*
+Relpath::initfunc(const char*& path, SVN::Pool& pool)
+{
+  path = svn_relpath__internal_style(path, pool.getPool());
+  return SVN_NO_ERROR;
+}

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/Path.h
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/Path.h?rev=1497326&r1=1497325&r2=1497326&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/Path.h (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/Path.h Thu Jun 27 12:33:21 2013
@@ -24,18 +24,19 @@
  * @brief Interface of the C++ class Path.
  */
 
-#ifndef PATH_H
-#define PATH_H
+#ifndef JAVAHL_PATH_H
+#define JAVAHL_PATH_H
 
 #include <string>
 #include <jni.h>
 #include "Pool.h"
 struct svn_error_t;
 
+
 /**
  * Encapsulation for Subversion Path handling
  */
-class Path
+class PathBase
 {
  private:
   // The path to be stored.
@@ -48,9 +49,11 @@ class Path
    *
    * @param pi_path Path string
    */
-  void init(const char *pi_path, SVN::Pool &in_pool);
+  void init(const char *pi_path,
+            svn_error_t* initfunc(const char*&, SVN::Pool&),
+            SVN::Pool &in_pool);
 
- public:
+ protected:
   /**
    * Constructor that takes a string as parameter.
    * The string is converted to subversion internal
@@ -58,27 +61,31 @@ class Path
    *
    * @param pi_path Path string
    */
-  Path(const std::string &pi_path, SVN::Pool &in_pool);
+  PathBase(const std::string &pi_path,
+           svn_error_t* initfunc(const char*&, SVN::Pool&),
+           SVN::Pool &in_pool);
 
   /**
    * Constructor
    *
-   * @see Path::Path (const std::string &)
+   * @see PathBase::PathBase (const std::string &)
    * @param pi_path Path string
    */
-  Path(const char *pi_path, SVN::Pool &in_pool);
+  PathBase(const char *pi_path,
+           svn_error_t* initfunc(const char*&, SVN::Pool&),
+           SVN::Pool &in_pool);
 
   /**
-   * Copy constructor
-   *
-   * @param pi_path Path to be copied
+   * Constructor from a Java string.
    */
-  Path(const Path &pi_path, SVN::Pool &in_pool);
+  PathBase(jstring jpath,
+           svn_error_t* initfunc(const char*&, SVN::Pool&),
+           SVN::Pool &in_pool);
 
   /**
    * Assignment operator
    */
-  Path &operator=(const Path&);
+  PathBase &operator=(const PathBase&);
 
   /**
    * @return Path string
@@ -92,6 +99,7 @@ class Path
 
   svn_error_t *error_occurred() const;
 
+public:
   /**
    * Returns whether @a path is non-NULL and passes the @c
    * svn_path_check_valid() test.
@@ -101,4 +109,113 @@ class Path
   static jboolean isValid(const char *path);
 };
 
-#endif  // PATH_H
+
+/**
+ * Dirent or URI
+ */
+class Path : protected PathBase
+{
+ public:
+  Path(const std::string &pi_path, SVN::Pool &in_pool)
+    : PathBase(pi_path, initfunc, in_pool)
+    {}
+
+  Path(const char *pi_path, SVN::Pool &in_pool)
+    : PathBase(pi_path, initfunc, in_pool)
+    {}
+
+  Path(jstring jpath, SVN::Pool &in_pool)
+    : PathBase(jpath, initfunc, in_pool)
+    {}
+
+  Path& operator=(const Path& that)
+    {
+      PathBase::operator=(that);
+      return *this;
+    }
+
+  const std::string &path() const { return PathBase::path(); }
+  const char *c_str() const { return PathBase::c_str(); }
+
+  svn_error_t *error_occurred() const
+    {
+      return PathBase::error_occurred();
+    }
+
+ private:
+  static svn_error_t* initfunc(const char*&, SVN::Pool&);
+};
+
+/**
+ * URL
+ */
+class URL : protected PathBase
+{
+ public:
+  URL(const std::string &pi_path, SVN::Pool &in_pool)
+    : PathBase(pi_path, initfunc, in_pool)
+    {}
+
+  URL(const char *pi_path, SVN::Pool &in_pool)
+    : PathBase(pi_path, initfunc, in_pool)
+    {}
+
+  URL(jstring jpath, SVN::Pool &in_pool)
+    : PathBase(jpath, initfunc, in_pool)
+    {}
+
+  URL& operator=(const URL& that)
+    {
+      PathBase::operator=(that);
+      return *this;
+    }
+
+  const std::string &path() const { return PathBase::path(); }
+  const char *c_str() const { return PathBase::c_str(); }
+
+  svn_error_t *error_occurred() const
+    {
+      return PathBase::error_occurred();
+    }
+
+ private:
+  static svn_error_t* initfunc(const char*&, SVN::Pool&);
+};
+
+/**
+ * Relative path
+ */
+class Relpath : protected PathBase
+{
+ public:
+  Relpath(const std::string &pi_path, SVN::Pool &in_pool)
+    : PathBase(pi_path, initfunc, in_pool)
+    {}
+
+  Relpath(const char *pi_path, SVN::Pool &in_pool)
+    : PathBase(pi_path, initfunc, in_pool)
+    {}
+
+  Relpath(jstring jpath, SVN::Pool &in_pool)
+    : PathBase(jpath, initfunc, in_pool)
+    {}
+
+  Relpath& operator=(const Relpath& that)
+    {
+      PathBase::operator=(that);
+      return *this;
+    }
+
+  const std::string &path() const { return PathBase::path(); }
+  const char *c_str() const { return PathBase::c_str(); }
+
+  svn_error_t *error_occurred() const
+    {
+      return PathBase::error_occurred();
+    }
+
+ private:
+  static svn_error_t* initfunc(const char*&, SVN::Pool&);
+};
+
+#endif  // JAVAHL_PATH_H

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/RemoteSession.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/RemoteSession.cpp?rev=1497326&r1=1497325&r2=1497326&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/RemoteSession.cpp (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/RemoteSession.cpp Thu Jun 27 12:33:21 2013
@@ -30,6 +30,7 @@
 #include "JNIByteArray.h"
 #include "JNIStringHolder.h"
 #include "JNIUtil.h"
+#include "Path.h"
 
 #include "svn_ra.h"
 #include "svn_string.h"
@@ -64,9 +65,11 @@ RemoteSession::open(jint jretryAttempts,
 {
   JNIEnv *env = JNIUtil::getEnv();
 
-  JNIStringHolder url(jurl);
+  SVN::Pool requestPool;
+  URL url(jurl, requestPool);
   if (JNIUtil::isExceptionThrown())
     return NULL;
+  SVN_JNI_ERR(url.error_occurred(), NULL);
   env->DeleteLocalRef(jurl);
 
   JNIStringHolder uuid(juuid);
@@ -98,7 +101,7 @@ RemoteSession::open(jint jretryAttempts,
     }
 
   jobject jremoteSession = open(
-      jretryAttempts, url, uuid, configDirectory,
+      jretryAttempts, url.c_str(), uuid, configDirectory,
       usernameStr, passwordStr, prompter, jprogress);
   if (JNIUtil::isExceptionThrown() || !jremoteSession)
     {
@@ -279,12 +282,13 @@ RemoteSession::dispose(jobject jthis)
 
 void RemoteSession::reparent(jstring jurl)
 {
-  JNIStringHolder url(jurl);
-  if (JNIUtil::isJavaExceptionThrown())
+  SVN::Pool subPool(pool);
+  URL url(jurl, subPool);
+  if (JNIUtil::isExceptionThrown())
     return;
+  SVN_JNI_ERR(url.error_occurred(),);
 
-  SVN::Pool subPool(pool);
-  SVN_JNI_ERR(svn_ra_reparent(m_session, url, subPool.getPool()), );
+  SVN_JNI_ERR(svn_ra_reparent(m_session, url.c_str(), subPool.getPool()), );
 }
 
 jstring
@@ -304,14 +308,15 @@ RemoteSession::getSessionUrl()
 jstring
 RemoteSession::getSessionRelativePath(jstring jurl)
 {
-  JNIStringHolder url(jurl);
-  if (JNIUtil::isJavaExceptionThrown())
+  SVN::Pool subPool(pool);
+  URL url(jurl, subPool);
+  if (JNIUtil::isExceptionThrown())
     return NULL;
+  SVN_JNI_ERR(url.error_occurred(), NULL);
 
-  SVN::Pool subPool(pool);
   const char* rel_path;
   SVN_JNI_ERR(svn_ra_get_path_relative_to_session(
-                  m_session, &rel_path, url, subPool.getPool()),
+                  m_session, &rel_path, url.c_str(), subPool.getPool()),
               NULL);
   jstring jrel_path = JNIUtil::makeJString(rel_path);
   if (JNIUtil::isJavaExceptionThrown())
@@ -323,13 +328,15 @@ RemoteSession::getSessionRelativePath(js
 jstring
 RemoteSession::getReposRelativePath(jstring jurl)
 {
-  JNIStringHolder url(jurl);
-  if (JNIUtil::isJavaExceptionThrown())
+  SVN::Pool subPool(pool);
+  URL url(jurl, subPool);
+  if (JNIUtil::isExceptionThrown())
     return NULL;
+  SVN_JNI_ERR(url.error_occurred(), NULL);
 
-  SVN::Pool subPool(pool);
   const char* rel_path;
-  SVN_JNI_ERR(svn_ra_get_path_relative_to_root(m_session, &rel_path, url,
+  SVN_JNI_ERR(svn_ra_get_path_relative_to_root(m_session, &rel_path,
+                                               url.c_str(),
                                                subPool.getPool()),
               NULL);
 
@@ -470,21 +477,22 @@ jlong
 RemoteSession::getFile(jlong jrevision, jstring jpath,
                        jobject jcontents, jobject jproperties)
 {
-  JNIStringHolder path(jpath);
+  OutputStream contents_proxy(jcontents);
   if (JNIUtil::isExceptionThrown())
     return SVN_INVALID_REVNUM;
 
-  OutputStream contents_proxy(jcontents);
+  SVN::Pool subPool(pool);
+  Relpath path(jpath, subPool);
   if (JNIUtil::isExceptionThrown())
     return SVN_INVALID_REVNUM;
+  SVN_JNI_ERR(path.error_occurred(), SVN_INVALID_REVNUM);
 
-  SVN::Pool subPool(pool);
   apr_hash_t* props = NULL;
   svn_revnum_t fetched_rev = svn_revnum_t(jrevision);
   svn_stream_t* contents = (!jcontents ? NULL
                             : contents_proxy.getStream(subPool));
 
-  SVN_JNI_ERR(svn_ra_get_file(m_session, path, fetched_rev,
+  SVN_JNI_ERR(svn_ra_get_file(m_session, path.c_str(), fetched_rev,
                               contents, &fetched_rev,
                               (jproperties ? &props : NULL),
                               subPool.getPool()),
@@ -578,18 +586,20 @@ RemoteSession::getDirectory(jlong jrevis
                             jint jdirent_fields, jobject jdirents,
                             jobject jproperties)
 {
-  JNIStringHolder path(jpath);
+  SVN::Pool subPool(pool);
+  Relpath path(jpath, subPool);
   if (JNIUtil::isExceptionThrown())
     return SVN_INVALID_REVNUM;
+  SVN_JNI_ERR(path.error_occurred(), SVN_INVALID_REVNUM);
 
-  SVN::Pool subPool(pool);
   apr_hash_t* props = NULL;
   apr_hash_t* dirents = NULL;
   svn_revnum_t fetched_rev = svn_revnum_t(jrevision);
 
   SVN_JNI_ERR(svn_ra_get_dir2(m_session, (jdirents ? &dirents : NULL),
                               &fetched_rev, (jproperties ? &props : NULL),
-                              path, fetched_rev, apr_uint32_t(jdirent_fields),
+                              path.c_str(), fetched_rev,
+                              apr_uint32_t(jdirent_fields),
                               subPool.getPool()),
               SVN_INVALID_REVNUM);
 
@@ -601,7 +611,8 @@ RemoteSession::getDirectory(jlong jrevis
       SVN_JNI_ERR(svn_ra_get_session_url(m_session, &base_url,
                                          subPool.getPool()),
                   SVN_INVALID_REVNUM);
-      fill_dirents(base_url, path, jdirents, dirents, subPool.getPool());
+      fill_dirents(base_url, path.c_str(), jdirents, dirents,
+                   subPool.getPool());
       if (JNIUtil::isExceptionThrown())
         return SVN_INVALID_REVNUM;
     }
@@ -617,30 +628,31 @@ RemoteSession::getDirectory(jlong jrevis
 }
 
 // TODO: getMergeinfo
-// TODO: doUpdate
-// TODO: doSwitch
+// TODO: update
+// TODO: switch
 
 jobject
-RemoteSession::doStatus(jstring jstatus_target,
-                        jlong jrevision, jobject jdepth,
-                        jobject jstatus_editor)
+RemoteSession::status(jstring jstatus_target,
+                      jlong jrevision, jobject jdepth,
+                      jobject jstatus_editor)
 {
   return NULL;
 }
 
-// TODO: doDiff
+// TODO: diff
 // TODO: getLog
 
 jobject
 RemoteSession::checkPath(jstring jpath, jlong jrevision)
 {
-  JNIStringHolder path(jpath);
+  SVN::Pool subPool(pool);
+  Relpath path(jpath, subPool);
   if (JNIUtil::isExceptionThrown())
     return NULL;
+  SVN_JNI_ERR(path.error_occurred(), NULL);
 
-  SVN::Pool subPool(pool);
   svn_node_kind_t kind;
-  SVN_JNI_ERR(svn_ra_check_path(m_session, path,
+  SVN_JNI_ERR(svn_ra_check_path(m_session, path.c_str(),
                                 svn_revnum_t(jrevision),
                                 &kind, subPool.getPool()),
               NULL);
@@ -659,17 +671,18 @@ RemoteSession::checkPath(jstring jpath, 
 jobject
 RemoteSession::getLocks(jstring jpath, jobject jdepth)
 {
-  JNIStringHolder path(jpath);
+  svn_depth_t depth = EnumMapper::toDepth(jdepth);
   if (JNIUtil::isExceptionThrown())
     return NULL;
 
-  svn_depth_t depth = EnumMapper::toDepth(jdepth);
+  SVN::Pool subPool(pool);
+  Relpath path(jpath, subPool);
   if (JNIUtil::isExceptionThrown())
     return NULL;
+  SVN_JNI_ERR(path.error_occurred(), NULL);
 
-  SVN::Pool subPool(pool);
   apr_hash_t *locks;
-  SVN_JNI_ERR(svn_ra_get_locks2(m_session, &locks, path, depth,
+  SVN_JNI_ERR(svn_ra_get_locks2(m_session, &locks, path.c_str(), depth,
                                 subPool.getPool()),
               NULL);
 

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/RemoteSession.h
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/RemoteSession.h?rev=1497326&r1=1497325&r2=1497326&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/RemoteSession.h (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/RemoteSession.h Thu Jun 27 12:33:21 2013
@@ -35,6 +35,8 @@
 #include "RemoteSessionContext.h"
 #include "Prompter.h"
 
+class CommitEditor;
+
 /*
  * This class wraps Ra based operations from svn_ra.h
  */
@@ -76,12 +78,12 @@ class RemoteSession : public SVNBase
     jlong getDirectory(jlong jrevision, jstring jpath, jint jdirent_fields,
                        jobject jdirents, jobject jproperties);
     // TODO: getMergeinfo
-    // TODO: doUpdate
-    // TODO: doSwitch
-    jobject doStatus(jstring jstatus_target,
-                     jlong jrevision, jobject jdepth,
-                     jobject jstatus_editor);
-    // TODO: doDiff
+    // TODO: update
+    // TODO: switch
+    jobject status(jstring jstatus_target,
+                   jlong jrevision, jobject jdepth,
+                   jobject jstatus_editor);
+    // TODO: diff
     // TODO: getLog
     jobject checkPath(jstring jpath, jlong jrevision);
     // TODO: stat
@@ -99,6 +101,7 @@ class RemoteSession : public SVNBase
     jboolean hasCapability(jstring capability);
 
   private:
+    friend class CommitEditor;
     RemoteSession(jobject*, int retryAttempts,
                   const char* url, const char* uuid,
                   const char* configDirectory,

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/SVNClient.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/SVNClient.cpp?rev=1497326&r1=1497325&r2=1497326&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/SVNClient.cpp (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/SVNClient.cpp Thu Jun 27 12:33:21 2013
@@ -1576,12 +1576,7 @@ SVNClient::openRemoteSession(const char*
        by creating a copy of the prompter here. */
     Prompter* prompter = new Prompter(context.getPrompter());
     if (!prompter)
-    {
-        /* context.getSelf() created a new global reference. */
-        JNIUtil::getEnv()->DeleteGlobalRef(jctx);
-        JNIUtil::throwNullPointerException("allocating Prompter");
-        return NULL;
-    }
+      return NULL;
 
     jobject jremoteSession = RemoteSession::open(
         retryAttempts, path_info.url.c_str(), path_info.uuid.c_str(),
@@ -1589,12 +1584,7 @@ SVNClient::openRemoteSession(const char*
         context.getUsername(), context.getPassword(),
         prompter, jctx);
     if (JNIUtil::isJavaExceptionThrown())
-    {
-        /* context.getSelf() created a new global reference. */
-        JNIUtil::getEnv()->DeleteGlobalRef(jctx);
-        jremoteSession = NULL;
-        delete prompter;
-    }
+      delete prompter;
 
     return jremoteSession;
 }

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteSession.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteSession.cpp?rev=1497326&r1=1497325&r2=1497326&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteSession.cpp (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteSession.cpp Thu Jun 27 12:33:21 2013
@@ -49,7 +49,7 @@ JNIEXPORT void JNICALL
 Java_org_apache_subversion_javahl_remote_RemoteSession_nativeDispose(
     JNIEnv *env, jobject jthis)
 {
-  JNIEntry(RemoteSession, dispose);
+  JNIEntry(RemoteSession, nativeDispose);
   RemoteSession *ras = RemoteSession::getCppObject(jthis);
   if (ras != NULL)
     ras->dispose(jthis);
@@ -214,11 +214,11 @@ Java_org_apache_subversion_javahl_remote
 }
 
 // TODO: getMergeinfo
-// TODO: doUpdate
-// TODO: doSwitch
+// TODO: update
+// TODO: switch
 
 JNIEXPORT jobject JNICALL
-Java_org_apache_subversion_javahl_remote_RemoteSession_doStatus(
+Java_org_apache_subversion_javahl_remote_RemoteSession_status(
     JNIEnv *env, jobject jthis, jstring jstatus_target,
     jlong jrevision, jobject jdepth, jobject jstatus_editor)
 {
@@ -226,10 +226,10 @@ Java_org_apache_subversion_javahl_remote
   RemoteSession *ras = RemoteSession::getCppObject(jthis);
   CPPADDR_NULL_PTR(ras, NULL);
 
-  return ras->doStatus(jstatus_target, jrevision, jdepth, jstatus_editor);
+  return ras->status(jstatus_target, jrevision, jdepth, jstatus_editor);
 }
 
-// TODO: doDiff
+// TODO: diff
 // TODO: getLog
 
 JNIEXPORT jobject JNICALL

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/ClientException.java
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/ClientException.java?rev=1497326&r1=1497325&r2=1497326&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/ClientException.java (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/ClientException.java Thu Jun 27 12:33:21 2013
@@ -22,6 +22,7 @@
  */
 
 package org.apache.subversion.javahl;
+import java.util.List;
 
 /**
  * This exception is thrown whenever something goes wrong in the
@@ -36,7 +37,54 @@ public class ClientException extends Nat
     // http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf
     // http://java.sun.com/j2se/1.5.0/docs/guide/serialization/spec/version.html#6678
     // http://java.sun.com/javase/6/docs/platform/serialization/spec/version.html#6678
-    private static final long serialVersionUID = 1L;
+    private static final long serialVersionUID = 2L;
+
+    /**
+     * Describes a single error message in a stack of messages
+     * associated with this exception.
+     * @since 1.9
+     */
+    public static final class ErrorMessage
+    {
+        ErrorMessage(int code, String message, boolean generic)
+        {
+            this.code = code;
+            this.message = message;
+            this.generic = generic;
+        }
+
+        /** @return The APR error code associated with the message. */
+        public final int getCode() { return code; }
+
+        /** @return The error message text. */
+        public final String getMessage() { return message; }
+
+        /** @return A flag indicating whether this is a generic
+            message for the APR error code, or a more specific message
+            generated by the native libraries. */
+        public final boolean isGeneric() { return generic; }
+
+        private final int code;
+        private final String message;
+        private final boolean generic;
+    };
+
+    /**
+     * This constructor is only used by the native library.
+     *
+     * @param message A description of the problem.
+     * @param source The error's source.
+     * @param aprError Any associated APR error code for a wrapped
+     *        <code>svn_error_t</code>.
+     * @param messageStack The whole stack of error messages
+     * @since 1.9
+     */
+    ClientException(String message, String source, int aprError,
+                    List<ErrorMessage> messageStack)
+    {
+        super(message, source, aprError);
+        this.messageStack = messageStack;
+    }
 
     /**
      * This constructor is only used by the native library.
@@ -48,7 +96,12 @@ public class ClientException extends Nat
      */
     ClientException(String message, String source, int aprError)
     {
-        super(message, source, aprError);
+        this(message, source, aprError, null);
+    }
+
+    public List<ErrorMessage> getAllMessages()
+    {
+        return messageStack;
     }
 
     /**
@@ -68,4 +121,6 @@ public class ClientException extends Nat
             return new ClientException(t.getMessage(), null, -1);
         }
     }
+
+    private final List<ErrorMessage> messageStack;
 }

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNEditor.java
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNEditor.java?rev=1497326&r1=1497325&r2=1497326&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNEditor.java (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNEditor.java Thu Jun 27 12:33:21 2013
@@ -27,7 +27,6 @@ import org.apache.subversion.javahl.type
 import org.apache.subversion.javahl.callback.*;
 
 import java.io.InputStream;
-import java.util.List;
 import java.util.Map;
 
 /**
@@ -296,7 +295,7 @@ public interface ISVNEditor
      *
      * @throws ClientException
      */
-    void rotate(List<RotatePair> elements) throws ClientException;
+    void rotate(Iterable<RotatePair> elements) throws ClientException;
 
     public static final class RotatePair
     {

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNRemote.java
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNRemote.java?rev=1497326&r1=1497325&r2=1497326&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNRemote.java (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNRemote.java Thu Jun 27 12:33:21 2013
@@ -28,6 +28,7 @@ import org.apache.subversion.javahl.call
 
 import java.util.Date;
 import java.util.Map;
+import java.util.Set;
 import java.io.OutputStream;
 
 /**
@@ -44,7 +45,7 @@ public interface ISVNRemote
     void dispose();
 
     /**
-     * Cancel the active operation.
+     * Cancel the active operation, including any ongoing edits.
      * @throws ClientException
      */
     void cancelOperation() throws ClientException;
@@ -156,10 +157,47 @@ public interface ISVNRemote
             throws ClientException;
 
     /**
-     * Create a commit editor instance, rooted at the current session URL.
-     * @throws ClientException
-     */
-    ISVNEditor getCommitEditor() throws ClientException;
+     * Return an editor for committing changes to the session's
+     * repository, setting the revision properties from
+     * <code>revisionProperties</code>. The revisions being committed
+     * against are passed to the editor functions. The root of the commit
+     * is the session's URL.
+     * <p>
+     * <code>revisionProperties</code> is a hash mapping property names to
+     * property values. The commit log message is expected to be in the
+     * {@link Property#REV_LOG} element.  <code>revisionProperties</code>
+     * can not contain either of {@link Property#REV_DATE} or
+     * {@link Property#REV_AUTHOR}.
+     * <p>
+     * Before {@link ISVNEditor#complete()} returns, but after the commit
+     * has succeeded, it will invoke <code>commitCallback</code> (if not
+     * <code>null</code>) with filled-in {@link CommitInfo}.  If
+     * <code>commitCallback</code> returns an error, that error will be
+     * returned from {@link ISVNEditor#complete()}, otherwise
+     * {@link ISVNEditor#complete()} will return successfully (unless it
+     * encountered an error before invoking <code>commitCallback</code>).
+     * The callback will not be called if the commit was a no-op
+     * (i.e., nothing was committed).
+     * <p>
+     * <code>lockTokens</code>, if not <code>null</code>, is a hash
+     * mapping paths (relative to the session's URL) to lock tokens.  The
+     * server checks that the correct token is provided for each
+     * committed, locked path.  <code>lockTokens</code> must live during
+     * the whole commit operation.
+     * <p>
+     * If <cpde>keepLocks</code> is <cpde>true</code>, then do not release
+     * locks on committed objects.  Else, automatically release such
+     * locks.
+     * <p>
+     * The caller may not perform any remote operations using this session
+     * before finishing the edit.
+     * @throws ClientException
+     */
+    ISVNEditor getCommitEditor(Map<String, byte[]> revisionProperties,
+                               CommitCallback commitCallback,
+                               Set<Lock> lockTokens,
+                               boolean keepLocks)
+            throws ClientException;
 
     /**
      * Fetch the contents and properties of file <code>path</code> at
@@ -242,8 +280,8 @@ public interface ISVNRemote
             throws ClientException;
 
     // TODO: getMergeinfo
-    // TODO: doUpdate
-    // TODO: doSwitch
+    // TODO: update
+    // TODO: switch
 
     /**
      * Ask for a description of the status of a working copy with
@@ -292,12 +330,12 @@ public interface ISVNRemote
      * <code>depth</code>.
      * @throws ClientException
      */
-    ISVNReporter doStatus(String statusTarget,
-                          long revision, Depth depth,
-                          ISVNEditor statusEditor)
+    ISVNReporter status(String statusTarget,
+                        long revision, Depth depth,
+                        ISVNEditor statusEditor)
             throws ClientException;
 
-    // TODO: doDiff
+    // TODO: diff
     // TODO: getLog
 
     /**

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/CommitEditor.java
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/CommitEditor.java?rev=1497326&r1=1497325&r2=1497326&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/CommitEditor.java (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/CommitEditor.java Thu Jun 27 12:33:21 2013
@@ -31,8 +31,8 @@ import org.apache.subversion.javahl.JNIO
 import org.apache.subversion.javahl.ClientException;
 
 import java.io.InputStream;
-import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Implementation of ISVNEditor that drives commits.
@@ -40,132 +40,127 @@ import java.util.Map;
  */
 public class CommitEditor extends JNIObject implements ISVNEditor
 {
-    public void dispose() {/* TODO */}
-
-    public void addDirectory(String relativePath,
-                             Iterable<String> children,
-                             Map<String, byte[]> properties,
-                             long replacesRevision)
-            throws ClientException
-    {
-        notimplemented("addDirectory");
-    }
-
-    public void addFile(String relativePath,
-                        Checksum checksum,
-                        InputStream contents,
-                        Map<String, byte[]> properties,
-                        long replacesRevision)
-            throws ClientException
-    {
-        notimplemented("addFile");
-    }
-
-    public void addSymlink(String relativePath,
-                           String target,
-                           Map<String, byte[]> properties,
-                           long replacesRevision)
-            throws ClientException
-    {
-        notimplemented("addSymlink");
-    }
-
-    public void addAbsent(String relativePath,
-                          NodeKind kind,
-                          long replacesRevision)
-            throws ClientException
-    {
-        notimplemented("addAbsent");
-    }
-
-    public void alterDirectory(String relativePath,
-                               long revision,
-                               Iterable<String> children,
-                               Map<String, byte[]> properties)
-            throws ClientException
-    {
-        notimplemented("alterDirectory");
-    }
-
-    public void alterFile(String relativePath,
-                          long revision,
-                          Checksum checksum,
-                          InputStream contents,
-                          Map<String, byte[]> properties)
-            throws ClientException
-    {
-        notimplemented("alterFile");
-    }
-
-    public void alterSymlink(String relativePath,
-                             long revision,
-                             String target,
-                             Map<String, byte[]> properties)
-            throws ClientException
+    public void dispose()
     {
-        notimplemented("alterSymlink");
+        session.disposeEditor(this);
+        nativeDispose();
     }
 
-    public void delete(String relativePath,
-                       long revision)
-            throws ClientException
-    {
-        notimplemented("delete");
-    }
+    public native void addDirectory(String relativePath,
+                                    Iterable<String> children,
+                                    Map<String, byte[]> properties,
+                                    long replacesRevision)
+            throws ClientException;
+
+    public native void addFile(String relativePath,
+                               Checksum checksum,
+                               InputStream contents,
+                               Map<String, byte[]> properties,
+                               long replacesRevision)
+            throws ClientException;
 
-    public void copy(String sourceRelativePath,
-                     long sourceRevision,
-                     String destinationRelativePath,
-                     long replacesRevision)
-            throws ClientException
-    {
-        notimplemented("copy");
-    }
+    /**
+     * <b>Note:</b> Not implemented.
+     */
+    public native void addSymlink(String relativePath,
+                                  String target,
+                                  Map<String, byte[]> properties,
+                                  long replacesRevision)
+            throws ClientException;
+
+    public native void addAbsent(String relativePath,
+                                 NodeKind kind,
+                                 long replacesRevision)
+            throws ClientException;
+
+    public native void alterDirectory(String relativePath,
+                                      long revision,
+                                      Iterable<String> children,
+                                      Map<String, byte[]> properties)
+            throws ClientException;
+
+    public native void alterFile(String relativePath,
+                                 long revision,
+                                 Checksum checksum,
+                                 InputStream contents,
+                                 Map<String, byte[]> properties)
+            throws ClientException;
 
-    public void move(String sourceRelativePath,
-                     long sourceRevision,
-                     String destinationRelativePath,
-                     long replacesRevision)
-            throws ClientException
-    {
-        notimplemented("move");
-    }
+    /**
+     * <b>Note:</b> Not implemented.
+     */
+    public native void alterSymlink(String relativePath,
+                                    long revision,
+                                    String target,
+                                    Map<String, byte[]> properties)
+            throws ClientException;
+   
+    public native void delete(String relativePath,
+                              long revision)
+            throws ClientException;
+
+    public native void copy(String sourceRelativePath,
+                            long sourceRevision,
+                            String destinationRelativePath,
+                            long replacesRevision)
+            throws ClientException;
+
+    public native void move(String sourceRelativePath,
+                            long sourceRevision,
+                            String destinationRelativePath,
+                            long replacesRevision)
+            throws ClientException;
 
-    public void rotate(List<RotatePair> elements) throws ClientException
-    {
-        notimplemented("rotate");
-    }
+    /**
+     * <b>Note:</b> Not implemented.
+     */
+    public native void rotate(Iterable<RotatePair> elements)
+            throws ClientException;
 
-    public void complete() throws ClientException
-    {
-        notimplemented("complete");
-    }
+    public native void complete() throws ClientException;
 
-    public void abort() throws ClientException
-    {
-        notimplemented("abort");
-    }
+    public native void abort() throws ClientException;
 
     /**
      * This factory method called from RemoteSession.getCommitEditor.
      */
-    static final CommitEditor createInstance(RemoteSession owner)
+    static final
+        CommitEditor createInstance(RemoteSession session,
+                                    Map<String, byte[]> revisionProperties,
+                                    CommitCallback commitCallback,
+                                    Set<Lock> lockTokens,
+                                    boolean keepLocks)
             throws ClientException
     {
-        // FIXME: temporary implementation
-        return new CommitEditor(0L);
+        long cppAddr = nativeCreateInstance(session, revisionProperties,
+                                            commitCallback, lockTokens, keepLocks);
+        return new CommitEditor(cppAddr, session);
     }
 
     /**
-     * This constructor is called from JNI to get an instance.
+     * This constructor is called from the factory to get an instance.
      */
-    protected CommitEditor(long cppAddr)
+    protected CommitEditor(long cppAddr, RemoteSession session)
     {
         super(cppAddr);
+        this.session = session;
     }
 
-    private void notimplemented(String name)
-    {
-        throw new RuntimeException("Not implemented: " + name);
-    }
+    /** Stores a reference to the session that created this editor. */
+    protected RemoteSession session;
+
+    @Override
+    public native void finalize() throws Throwable;
+
+    /*
+     * Wrapped private native implementation declarations.
+     */
+    private native void nativeDispose();
+    private static final native
+        long nativeCreateInstance(RemoteSession session,
+                                  Map<String, byte[]> revisionProperties,
+                                  CommitCallback commitCallback,
+                                  Set<Lock> lockTokens,
+                                  boolean keepLocks)
+            throws ClientException;
 }

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteSession.java
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteSession.java?rev=1497326&r1=1497325&r2=1497326&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteSession.java (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteSession.java Thu Jun 27 12:33:21 2013
@@ -34,9 +34,9 @@ import org.apache.subversion.javahl.Oper
 import org.apache.subversion.javahl.ClientException;
 
 import java.lang.ref.WeakReference;
-import java.util.HashSet;
 import java.util.Date;
 import java.util.Map;
+import java.util.Set;
 import java.io.OutputStream;
 
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -46,17 +46,16 @@ public class RemoteSession extends JNIOb
 {
     public void dispose()
     {
-        if (editors != null)
+        if (editorReference != null)
         {
-            // Deactivate all open editors
-            for (WeakReference<ISVNEditor> ref : editors)
+            // Deactivate the open editor
+            ISVNEditor ed = editorReference.get();
+            if (ed != null)
             {
-                ISVNEditor ed = ref.get();
-                if (ed == null)
-                    continue;
                 ed.dispose();
-                ref.clear();
+                editorReference.clear();
             }
+            editorReference = null;
         }
         nativeDispose();
     }
@@ -109,12 +108,22 @@ public class RemoteSession extends JNIOb
     public native byte[] getRevisionProperty(long revision, String propertyName)
             throws ClientException;
 
-    public ISVNEditor getCommitEditor() throws ClientException
+    public ISVNEditor getCommitEditor(Map<String, byte[]> revisionProperties,
+                                      CommitCallback commitCallback,
+                                      Set<Lock> lockTokens,
+                                      boolean keepLocks)
+            throws ClientException
     {
-        ISVNEditor ed = CommitEditor.createInstance(this);
-        if (editors == null)
-            editors = new HashSet<WeakReference<ISVNEditor>>();
-        editors.add(new WeakReference<ISVNEditor>(ed));
+        if (editorReference != null && editorReference.get() != null)
+            throw new IllegalStateException("An editor is already active");
+
+        ISVNEditor ed =
+            CommitEditor.createInstance(this, revisionProperties,
+                                        commitCallback, lockTokens, keepLocks);
+
+        if (editorReference != null)
+            editorReference.clear();
+        editorReference = new WeakReference<ISVNEditor>(ed);
         return ed;
     }
 
@@ -143,15 +152,15 @@ public class RemoteSession extends JNIOb
     }
 
     // TODO: getMergeinfo
-    // TODO: doUpdate
-    // TODO: doSwitch
+    // TODO: update
+    // TODO: switch
 
-    public native ISVNReporter doStatus(String statusTarget,
-                                        long revision, Depth depth,
-                                        ISVNEditor statusEditor)
+    public native ISVNReporter status(String statusTarget,
+                                      long revision, Depth depth,
+                                      ISVNEditor statusEditor)
             throws ClientException;
 
-    // TODO: doDiff
+    // TODO: diff
     // TODO: getLog
 
     public native NodeKind checkPath(String path, long revision)
@@ -219,10 +228,28 @@ public class RemoteSession extends JNIOb
     private class RemoteSessionContext extends OperationContext {}
 
     /*
-     * The set of open editors. We need this in order to dispose/abort
-     * the editors when the session is disposed.
+     * A reference to the current open editor. We need this in order
+     * to dispose/abort the editor when the session is disposed. And
+     * furthermore, there can be only one editor active at any time.
      */
-    private HashSet<WeakReference<ISVNEditor>> editors;
+    private WeakReference<ISVNEditor> editorReference;
+
+    /*
+     * The commit editor callse this when disposed to clear the
+     * reference. Note that this function will be called during our
+     * dispose, so make sure they don't step on each others' toes.
+     */
+    void disposeEditor(CommitEditor editor)
+    {
+        if (editorReference == null)
+            return;
+        ISVNEditor ed = editorReference.get();
+        if (ed == null)
+            return;
+        if (ed != editor)
+            throw new IllegalStateException("Disposing unknown editor");
+        editorReference.clear();
+    }
 
     /*
      * Private helper methods.

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java?rev=1497326&r1=1497325&r2=1497326&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java Thu Jun 27 12:33:21 2013
@@ -24,17 +24,22 @@ package org.apache.subversion.javahl;
 
 import org.apache.subversion.javahl.*;
 import org.apache.subversion.javahl.remote.*;
+import org.apache.subversion.javahl.callback.*;
 import org.apache.subversion.javahl.types.*;
 
 import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.Set;
 import java.util.Map;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 
 /**
  * This class is used for testing the SVNReposAccess class
@@ -237,13 +242,13 @@ public class SVNRemoteTests extends SVNT
     public void testGetCommitEditor() throws Exception
     {
         ISVNRemote session = getSession();
-        session.getCommitEditor();
+        session.getCommitEditor(null, null, null, false);
     }
 
     public void testDisposeCommitEditor() throws Exception
     {
         ISVNRemote session = getSession();
-        session.getCommitEditor();
+        session.getCommitEditor(null, null, null, false);
         session.dispose();
     }
 
@@ -300,10 +305,8 @@ public class SVNRemoteTests extends SVNT
         }
         catch (ClientException ex)
         {
-            String msg = ex.getMessage();
-            int index = msg.indexOf('\n');
             assertEquals("Disabled repository feature",
-                         msg.substring(0, index));
+                         ex.getAllMessages().get(0).getMessage());
             return;
         }
 
@@ -370,4 +373,313 @@ public class SVNRemoteTests extends SVNT
         for (Map.Entry<String, byte[]> e : properties.entrySet())
             assertTrue(e.getKey().startsWith("svn:entry:"));
     }
+
+    private final class CommitContext implements CommitCallback
+    {
+        public final ISVNEditor editor;
+        public CommitContext(ISVNRemote session, String logstr)
+            throws ClientException
+        {
+            Charset UTF8 = Charset.forName("UTF-8");
+            byte[] log = (logstr == null
+                          ? new byte[0]
+                          : logstr.getBytes(UTF8));
+            HashMap<String, byte[]> revprops = new HashMap<String, byte[]>();
+            revprops.put("svn:log", log);
+            editor = session.getCommitEditor(revprops, this, null, false);
+        }
+
+        public void commitInfo(CommitInfo info) { this.info = info; }
+        public long getRevision() { return info.getRevision(); }
+
+        private CommitInfo info;
+    }
+
+    public void testEditorCopy() throws Exception
+    {
+        ISVNRemote session = getSession();
+        CommitContext cc =
+            new CommitContext(session, "Copy A/B/lambda -> A/B/omega");
+
+        try {
+            // FIXME: alter dir A/B first
+            cc.editor.copy("A/B/lambda", 1, "A/B/omega",
+                           Revision.SVN_INVALID_REVNUM);
+            cc.editor.complete();
+        } finally {
+            cc.editor.dispose();
+        }
+
+        assertEquals(2, cc.getRevision());
+        assertEquals(2, session.getLatestRevision());
+        assertEquals(NodeKind.file,
+                     session.checkPath("A/B/lambda",
+                                       Revision.SVN_INVALID_REVNUM));
+        assertEquals(NodeKind.file,
+                     session.checkPath("A/B/omega",
+                                       Revision.SVN_INVALID_REVNUM));
+    }
+
+    public void testEditorMove() throws Exception
+    {
+        ISVNRemote session = getSession();
+        CommitContext cc =
+            new CommitContext(session, "Move A/B/lambda -> A/B/omega");
+
+        try {
+            // FIXME: alter dir A/B first
+            cc.editor.move("A/B/lambda", 1, "A/B/omega",
+                           Revision.SVN_INVALID_REVNUM);
+            cc.editor.complete();
+        } finally {
+            cc.editor.dispose();
+        }
+
+        assertEquals(2, cc.getRevision());
+        assertEquals(2, session.getLatestRevision());
+        assertEquals(NodeKind.none,
+                     session.checkPath("A/B/lambda",
+                                       Revision.SVN_INVALID_REVNUM));
+        assertEquals(NodeKind.file,
+                     session.checkPath("A/B/omega",
+                                       Revision.SVN_INVALID_REVNUM));
+    }
+
+    public void testEditorDelete() throws Exception
+    {
+        ISVNRemote session = getSession();
+        CommitContext cc =
+            new CommitContext(session, "Delete all greek files");
+
+        String[] filePaths = { "iota",
+                               "A/mu",
+                               "A/B/lambda",
+                               "A/B/E/alpha",
+                               "A/B/E/beta",
+                               "A/D/gamma",
+                               "A/D/G/pi",
+                               "A/D/G/rho",
+                               "A/D/G/tau",
+                               "A/D/H/chi",
+                               "A/D/H/omega",
+                               "A/D/H/psi" };
+
+        try {
+            // FIXME: alter a bunch of dirs first
+            for (String path : filePaths)
+                cc.editor.delete(path, 1);
+            cc.editor.complete();
+        } finally {
+            cc.editor.dispose();
+        }
+
+        assertEquals(2, cc.getRevision());
+        assertEquals(2, session.getLatestRevision());
+        for (String path : filePaths)
+            assertEquals(NodeKind.none,
+                         session.checkPath(path, Revision.SVN_INVALID_REVNUM));
+    }
+
+    public void testEditorMkdir() throws Exception
+    {
+        ISVNRemote session = getSession();
+        CommitContext cc = new CommitContext(session, "Make hebrew dir");
+
+        try {
+            // FIXME: alter dir . first
+            cc.editor.addDirectory("ALEPH",
+                                   new ArrayList<String>(),
+                                   new HashMap<String, byte[]>(),
+                                   Revision.SVN_INVALID_REVNUM);
+            cc.editor.complete();
+        } finally {
+            cc.editor.dispose();
+        }
+
+        assertEquals(2, cc.getRevision());
+        assertEquals(2, session.getLatestRevision());
+        assertEquals(NodeKind.dir,
+                     session.checkPath("ALEPH",
+                                       Revision.SVN_INVALID_REVNUM));
+    }
+
+    public void testEditorSetDirProps() throws Exception
+    {
+        Charset UTF8 = Charset.forName("UTF-8");
+        ISVNRemote session = getSession();
+
+        byte[] ignoreval = "*.pyc\n.gitignore\n".getBytes(UTF8);
+        HashMap<String, byte[]> props = new HashMap<String, byte[]>();
+        props.put("svn:ignore", ignoreval);
+
+        CommitContext cc = new CommitContext(session, "Add svn:ignore");
+        try {
+            cc.editor.alterDirectory("", 1, null, props);
+            cc.editor.complete();
+        } finally {
+            cc.editor.dispose();
+        }
+
+        assertEquals(2, cc.getRevision());
+        assertEquals(2, session.getLatestRevision());
+        assertTrue(Arrays.equals(ignoreval,
+                                 client.propertyGet(session.getSessionUrl(),
+                                                    "svn:ignore",
+                                                    Revision.HEAD,
+                                                    Revision.HEAD)));
+    }
+
+    private static byte[] SHA1(byte[] text) throws NoSuchAlgorithmException
+    {
+        MessageDigest md = MessageDigest.getInstance("SHA-1");
+        return md.digest(text);
+    }
+
+    public void testEditorAddFile() throws Exception
+    {
+        Charset UTF8 = Charset.forName("UTF-8");
+        ISVNRemote session = getSession();
+
+        byte[] eolstyle = "native".getBytes(UTF8);
+        HashMap<String, byte[]> props = new HashMap<String, byte[]>();
+        props.put("svn:eol-style", eolstyle);
+
+        byte[] contents = "This is file 'xi'.".getBytes(UTF8);
+        Checksum hash = new Checksum(SHA1(contents), Checksum.Kind.SHA1);
+        ByteArrayInputStream stream = new ByteArrayInputStream(contents);
+
+        CommitContext cc = new CommitContext(session, "Add A/xi");
+        try {
+            // FIXME: alter dir A first
+            cc.editor.addFile("A/xi", hash, stream, props,
+                              Revision.SVN_INVALID_REVNUM);
+            cc.editor.complete();
+        } finally {
+            cc.editor.dispose();
+        }
+
+        assertEquals(2, cc.getRevision());
+        assertEquals(2, session.getLatestRevision());
+        assertEquals(NodeKind.file,
+                     session.checkPath("A/xi",
+                                       Revision.SVN_INVALID_REVNUM));
+
+        byte[] propval = client.propertyGet(session.getSessionUrl() + "/A/xi",
+                                            "svn:eol-style",
+                                            Revision.HEAD,
+                                            Revision.HEAD);
+        assertTrue(Arrays.equals(eolstyle, propval));
+    }
+
+    public void testEditorSetFileProps() throws Exception
+    {
+        Charset UTF8 = Charset.forName("UTF-8");
+        ISVNRemote session = getSession();
+
+        byte[] eolstyle = "CRLF".getBytes(UTF8);
+        HashMap<String, byte[]> props = new HashMap<String, byte[]>();
+        props.put("svn:eol-style", eolstyle);
+
+        CommitContext cc =
+            new CommitContext(session, "Change eol-style on A/B/E/alpha");
+        try {
+            cc.editor.alterFile("A/B/E/alpha", 1, null, null, props);
+            cc.editor.complete();
+        } finally {
+            cc.editor.dispose();
+        }
+
+        assertEquals(2, cc.getRevision());
+        assertEquals(2, session.getLatestRevision());
+        byte[] propval = client.propertyGet(session.getSessionUrl()
+                                            + "/A/B/E/alpha",
+                                            "svn:eol-style",
+                                            Revision.HEAD,
+                                            Revision.HEAD);
+        assertTrue(Arrays.equals(eolstyle, propval));
+    }
+
+    // public void testEditorRotate() throws Exception
+    // {
+    //     ISVNRemote session = getSession();
+    //
+    //     ArrayList<ISVNEditor.RotatePair> rotation =
+    //         new ArrayList<ISVNEditor.RotatePair>(3);
+    //     rotation.add(new ISVNEditor.RotatePair("A/B", 1));
+    //     rotation.add(new ISVNEditor.RotatePair("A/C", 1));
+    //     rotation.add(new ISVNEditor.RotatePair("A/D", 1));
+    //
+    //     CommitContext cc =
+    //         new CommitContext(session, "Rotate A/B -> A/C -> A/D");
+    //     try {
+    //         // No alter-dir of A is needed, children remain the same.
+    //         cc.editor.rotate(rotation);
+    //         cc.editor.complete();
+    //     } finally {
+    //         cc.editor.dispose();
+    //     }
+    //
+    //     assertEquals(2, cc.getRevision());
+    //     assertEquals(2, session.getLatestRevision());
+    //
+    //     HashMap<String, DirEntry> dirents = new HashMap<String, DirEntry>();
+    //     HashMap<String, byte[]> properties = new HashMap<String, byte[]>();
+    //
+    //     // A/B is now what used to be A/D, so A/B/H must exist
+    //     session.getDirectory(Revision.SVN_INVALID_REVNUM, "A/B",
+    //                          DirEntry.Fields.all, dirents, properties);
+    //     assertEquals(dirents.get("H").getPath(), "H");
+    //
+    //     // A/C is now what used to be A/B, so A/C/F must exist
+    //     session.getDirectory(Revision.SVN_INVALID_REVNUM, "A/C",
+    //                          DirEntry.Fields.all, dirents, properties);
+    //     assertEquals(dirents.get("F").getPath(), "F");
+    //
+    //     // A/D is now what used to be A/C and must be empty
+    //     session.getDirectory(Revision.SVN_INVALID_REVNUM, "A/D",
+    //                          DirEntry.Fields.all, dirents, properties);
+    //     assertTrue(dirents.isEmpty());
+    // }
+
+    // Sanity check so that we don't forget about unimplemented methods.
+    public void testEditorNotImplemented() throws Exception
+    {
+        ISVNRemote session = getSession();
+
+        HashMap<String, byte[]> props = new HashMap<String, byte[]>();
+        ArrayList<ISVNEditor.RotatePair> rotation =
+            new ArrayList<ISVNEditor.RotatePair>();
+
+        CommitContext cc = new CommitContext(session, "not implemented");
+        try {
+            String exmsg;
+
+            try {
+                exmsg = "";
+                cc.editor.addSymlink("", "", props, 1);
+            } catch (IllegalStateException ex) {
+                exmsg = ex.getMessage();
+            }
+            assertEquals("Not implemented: CommitEditor.addSymlink", exmsg);
+
+            try {
+                exmsg = "";
+                cc.editor.alterSymlink("", 1, "", null);
+            } catch (IllegalStateException ex) {
+                exmsg = ex.getMessage();
+            }
+            assertEquals("Not implemented: CommitEditor.alterSymlink", exmsg);
+
+            try {
+                exmsg = "";
+                cc.editor.rotate(rotation);
+            } catch (IllegalStateException ex) {
+                exmsg = ex.getMessage();
+            }
+            assertEquals("Not implemented: CommitEditor.rotate", exmsg);
+        } finally {
+            cc.editor.dispose();
+        }
+
+    }
 }