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/11/21 01:22:34 UTC

svn commit: r1543993 - in /subversion/branches/javahl-1.8-extensions: ./ subversion/bindings/javahl/ subversion/bindings/javahl/native/ subversion/bindings/javahl/native/jniwrapper/ subversion/bindings/javahl/src/org/apache/subversion/javahl/ subversio...

Author: brane
Date: Thu Nov 21 00:22:33 2013
New Revision: 1543993

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

Added:
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/NativeStream.cpp
      - copied unchanged from r1543991, subversion/trunk/subversion/bindings/javahl/native/NativeStream.cpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/NativeStream.hpp
      - copied unchanged from r1543991, subversion/trunk/subversion/bindings/javahl/native/NativeStream.hpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_io_stream.cpp
      - copied unchanged from r1543991, subversion/trunk/subversion/bindings/javahl/native/jniwrapper/jni_io_stream.cpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_io_stream.hpp
      - copied unchanged from r1543991, subversion/trunk/subversion/bindings/javahl/native/jniwrapper/jni_io_stream.hpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_string_map.cpp
      - copied unchanged from r1543991, subversion/trunk/subversion/bindings/javahl/native/jniwrapper/jni_string_map.cpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_string_map.hpp
      - copied unchanged from r1543991, subversion/trunk/subversion/bindings/javahl/native/jniwrapper/jni_string_map.hpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_SubstLib.cpp
      - copied unchanged from r1543991, subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_SubstLib.cpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/NativeInputStream.java
      - copied unchanged from r1543991, subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/NativeInputStream.java
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/NativeOutputStream.java
      - copied unchanged from r1543991, subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/NativeOutputStream.java
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/SubstLib.java
      - copied unchanged from r1543991, subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/SubstLib.java
Removed:
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_javahl_exception.hpp
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/ExternalItem.cpp
    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/SVNClient.cpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_base.cpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_class_cache.cpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_env.hpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_list.cpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_list.hpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_PropLib.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/ISVNClient.java
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeException.java
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/SVNUtil.java
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/SubversionException.java
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/ExternalItem.java
    subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/tests/org/apache/subversion/javahl/UtilTests.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=1543993&r1=1543992&r2=1543993&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/build.conf (original)
+++ subversion/branches/javahl-1.8-extensions/build.conf Thu Nov 21 00:22:33 2013
@@ -58,6 +58,8 @@ private-built-includes =
         subversion/bindings/javahl/include/org_apache_subversion_javahl_Path.h
         subversion/bindings/javahl/include/org_apache_subversion_javahl_SVNRepos.h
         subversion/bindings/javahl/include/org_apache_subversion_javahl_SVNClient.h
+        subversion/bindings/javahl/include/org_apache_subversion_javahl_types_NativeInputStream.h
+        subversion/bindings/javahl/include/org_apache_subversion_javahl_types_NativeOutputStream.h
         subversion/bindings/javahl/include/org_apache_subversion_javahl_types_Version.h
         subversion/bindings/javahl/include/org_apache_subversion_javahl_types_VersionExtended.h
         subversion/bindings/javahl/include/org_apache_subversion_javahl_types_VersionExtended_LinkedLib.h
@@ -75,6 +77,7 @@ private-built-includes =
         subversion/bindings/javahl/include/org_apache_subversion_javahl_util_ConfigLib.h
         subversion/bindings/javahl/include/org_apache_subversion_javahl_util_DiffLib.h
         subversion/bindings/javahl/include/org_apache_subversion_javahl_util_PropLib.h
+        subversion/bindings/javahl/include/org_apache_subversion_javahl_util_SubstLib.h
 
 test-scripts =
         subversion/tests/cmdline/*_tests.py

Propchange: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/
------------------------------------------------------------------------------
  Merged /subversion/trunk/subversion/bindings/javahl:r1540992-1543991

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/ExternalItem.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/ExternalItem.cpp?rev=1543993&r1=1543992&r2=1543993&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/ExternalItem.cpp (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/ExternalItem.cpp Thu Nov 21 00:22:33 2013
@@ -98,7 +98,7 @@ ExternalItem::get_external_item(SVN::Poo
 {
   svn_wc_external_item2_t* item;
   apr_pool_t* const pool = svnpool.getPool();
-  SVN_JAVAHL_CHECK(svn_wc_external_item2_create(&item, pool));
+  SVN_JAVAHL_CHECK(m_env, svn_wc_external_item2_create(&item, pool));
 
   item->target_dir = apr_pstrdup(
       pool, Java::String::Contents(m_target_dir).c_str());
@@ -135,7 +135,8 @@ jobject Revision::makeJRevision(const sv
     {
       const jclass cls = env.FindClass(
           JAVA_PACKAGE"/types/Revision$DateSpec");
-      return env.NewObject(cls, env.GetMethodID(cls, "<init>", "(J)V"));
+      return env.NewObject(cls, env.GetMethodID(cls, "<init>", "(J)V"),
+                           jlong(rev.value.date / 1000));
     }
 
   const jclass cls = env.FindClass(JAVA_PACKAGE"/types/Revision");

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=1543993&r1=1543992&r2=1543993&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 Nov 21 00:22:33 2013
@@ -659,7 +659,7 @@ std::string JNIUtil::makeSVNErrorMessage
   return buffer;
 }
 
-void JNIUtil::wrappedHandleSVNError(svn_error_t *err)
+void JNIUtil::wrappedHandleSVNError(svn_error_t *err, jthrowable jcause)
 {
   jstring jmessage;
   jobject jstack;
@@ -716,12 +716,14 @@ void JNIUtil::wrappedHandleSVNError(svn_
 
   jmethodID mid = env->GetMethodID(clazz, "<init>",
                                    "(Ljava/lang/String;"
+                                   "Ljava/lang/Throwable;"
                                    "Ljava/lang/String;I"
                                    "Ljava/util/List;)V");
   if (isJavaExceptionThrown())
     POP_AND_RETURN_NOTHING();
-  jobject nativeException = env->NewObject(clazz, mid, jmessage, jsource,
-                                           jint(err->apr_err), jstack);
+  jobject nativeException = env->NewObject(clazz, mid, jmessage, jcause,
+                                           jsource, jint(err->apr_err),
+                                           jstack);
   if (isJavaExceptionThrown())
     POP_AND_RETURN_NOTHING();
 
@@ -792,10 +794,10 @@ void JNIUtil::wrappedHandleSVNError(svn_
   env->Throw(static_cast<jthrowable>(env->PopLocalFrame(nativeException)));
 }
 
-void JNIUtil::handleSVNError(svn_error_t *err)
+void JNIUtil::handleSVNError(svn_error_t *err, jthrowable jcause)
 {
   try {
-    wrappedHandleSVNError(err);
+    wrappedHandleSVNError(err, jcause);
   } catch (...) {
     svn_error_clear(err);
     throw;

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=1543993&r1=1543992&r2=1543993&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 Nov 21 00:22:33 2013
@@ -121,7 +121,7 @@ class JNIUtil
    * Throw a Java exception corresponding to err, and run
    * svn_error_clear() on err.
    */
-  static void handleSVNError(svn_error_t *err);
+  static void handleSVNError(svn_error_t *err, jthrowable jcause = NULL);
 
   static std::string makeSVNErrorMessage(svn_error_t *err,
                                          jstring *jerror_message,
@@ -158,7 +158,7 @@ class JNIUtil
   static JNIMutex *g_configMutex;
 
  private:
-  static void wrappedHandleSVNError(svn_error_t *err);
+  static void wrappedHandleSVNError(svn_error_t *err, jthrowable jcause);
   static void putErrorsInTrace(svn_error_t *err,
                                std::vector<jobject> &stackTrace);
 

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=1543993&r1=1543992&r2=1543993&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 Nov 21 00:22:33 2013
@@ -449,12 +449,7 @@ void SVNClient::copy(CopySources &copySo
     SVN::Pool subPool(pool);
 
     apr_array_header_t *srcs = copySources.array(subPool);
-    if (srcs == NULL)
-    {
-        JNIUtil::throwNativeException(JAVA_PACKAGE "/ClientException",
-                                      "Invalid copy sources");
-        return;
-    }
+    SVN_JNI_NULL_PTR_EX(srcs, "sources", );
     SVN_JNI_NULL_PTR_EX(destPath, "destPath", );
     Path destinationPath(destPath, subPool);
     SVN_JNI_ERR(destinationPath.error_occurred(), );

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_base.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_base.cpp?rev=1543993&r1=1543992&r2=1543993&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_base.cpp (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_base.cpp Thu Nov 21 00:22:33 2013
@@ -27,7 +27,9 @@
 #include "jni_object.hpp"
 #include "jni_string.hpp"
 #include "jni_array.hpp"
+#include "jni_stack.hpp"
 
+#include "../JNIUtil.h"
 
 // Global library initializaiton
 
@@ -193,7 +195,6 @@ void Exception::static_init(Env env)
       "getMessage", "()Ljava/lang/String;");
 }
 
-
 // Other exception class initializers
 
 const char* const RuntimeException::m_class_name =
@@ -205,32 +206,41 @@ const char* const NullPointerException::
 const char* const OutOfMemoryError::m_class_name =
   "java/lang/OutOfMemoryError";
 
+const char* const IndexOutOfBoundsException::m_class_name =
+  "java/lang/IndexOutOfBoundsException";
+
 const char* const IOException::m_class_name =
   "java/io/IOException";
 
-} // namespace Java
-
 
-namespace JavaHL {
+// Implementation of jni_stack.hpp
 
-// class JavaHL::JavaException
-
-JavaException::~JavaException() throw() {}
-
-const char* JavaException::what() const throw()
+void handle_svn_error(Env env, ::svn_error_t* err)
 {
-  // FIXME: Implement this?
-  return "";
-}
+  jthrowable cause = NULL;
 
-jthrowable JavaException::get_java_exception() const
-{
-  return ::Java::Env().ExceptionOccurred();
-}
+  // If the exception being currently thrown was generated by the
+  // JavaHL bindings, then assume the error was propagated through
+  // native code and do not re-throw it.
+  if (env.ExceptionCheck())
+    {
+      cause = env.ExceptionOccurred();
+      if (env.IsInstanceOf(cause, ClassCache::get_subversion_exception()))
+        {
+          // XXX FIXME: Should really have a special error code
+          // specifically for propagating Java exceptions from
+          // callbacks through native code.
+          svn_error_clear(err);
+          throw SignalExceptionThrown();
+        }
+    }
 
-jthrowable JavaException::get_java_exception(const ::Java::Env& env) const
-{
-  return env.ExceptionOccurred();
+  // Make sure there's only a single exception in the environment.
+  if (cause)
+    env.ExceptionClear();
+
+  ::JNIUtil::handleSVNError(err, cause);
+  throw SignalExceptionThrown();
 }
 
-} // namespace JavaHL
+} // namespace Java

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_class_cache.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_class_cache.cpp?rev=1543993&r1=1543992&r2=1543993&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_class_cache.cpp (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_class_cache.cpp Thu Nov 21 00:22:33 2013
@@ -21,6 +21,9 @@
  * @endcopyright
  */
 
+#include <stdexcept>
+
+#define SVN_JAVAHL_JNIWRAPPER_LOG(expr)
 #include "jni_env.hpp"
 #include "jni_globalref.hpp"
 #include "jni_exception.hpp"
@@ -35,7 +38,40 @@ const ClassCache* ClassCache::m_instance
 
 void ClassCache::create()
 {
-  new ClassCache(Env());
+  const char* exception_message = NULL;
+
+  try
+    {
+      new ClassCache(Env());
+    }
+  catch (const std::exception& ex)
+    {
+      exception_message = ex.what();
+    }
+  catch (...)
+    {
+      exception_message = "Caught unknown C++ exception";
+    }
+
+  // Use the raw environment without exception checks here
+  ::JNIEnv* const jenv = Env().get();
+  if (exception_message || jenv->ExceptionCheck())
+    {
+      const jclass rtx = jenv->FindClass("java/lang/RuntimeException");
+      const jmethodID ctor = jenv->GetMethodID(rtx, "<init>",
+                                               "(Ljava/lang/String;"
+                                               "Ljava/lang/Throwable;)V");
+      jobject cause = jenv->ExceptionOccurred();
+      if (!cause && exception_message)
+        {
+          const jstring msg = jenv->NewStringUTF(exception_message);
+          cause = jenv->NewObject(rtx, ctor, msg, jthrowable(0));
+        }
+      const jstring reason =
+        jenv->NewStringUTF("JavaHL native library initialization failed");
+      const jobject exception = jenv->NewObject(rtx, ctor, reason, cause);
+      jenv->Throw(jthrowable(exception));
+    }
 }
 
 void ClassCache::destroy()

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_env.hpp
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_env.hpp?rev=1543993&r1=1543992&r2=1543993&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_env.hpp (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_env.hpp Thu Nov 21 00:22:33 2013
@@ -30,21 +30,30 @@
 
 #include "svn_private_config.h"
 
-#include "jni_javahl_exception.hpp"
-
 #ifdef SVN_JAVAHL_DEBUG
-#include <iostream>
-#define SVN_JAVAHL_JNIWRAPPER_LOG(expr)       \
-  do {                                        \
-    std::cerr << expr << std::endl;           \
-  } while(0)
+#  ifndef SVN_JAVAHL_JNIWRAPPER_LOG
+#    include <iostream>
+#    define SVN_JAVAHL_JNIWRAPPER_LOG(expr)      \
+       (std::cerr << expr << std::endl)
+#  endif // SVN_JAVAHL_JNIWRAPPER_LOG
 #else
-#define SVN_JAVAHL_JNIWRAPPER_LOG(expr)
+#  define SVN_JAVAHL_JNIWRAPPER_LOG(expr)
 #endif // SVN_JAVAHL_DEBUG
 
 namespace Java {
 
 /**
+ * A C++ exception object for signalling that a Java exception has
+ * been thrown.
+ *
+ * Thrown to unwind the stack while avoiding code clutter when a Java
+ * exception is detected in the JNI environment.
+ *
+ * @since New in 1.9.
+ */
+class SignalExceptionThrown {};
+
+/**
  * Auto-initializing proxy for the JNI method ID.
  *
  * Behaves like a @c jmethodID but automatically initializes to @c NULL.
@@ -214,24 +223,36 @@ public:
     }
 
   /** Wrapped JNI function. */
-  jint Throw(jthrowable exc) const
+  jint Throw(jthrowable exc) const throw()
     {
       return m_env->Throw(exc);
     }
 
   /** Wrapped JNI function. */
-  jint ThrowNew(jclass cls, const char* message) const
+  jint ThrowNew(jclass cls, const char* message) const throw()
     {
       return m_env->ThrowNew(cls, message);
     }
 
   /** Wrapped JNI function. */
+  jboolean ExceptionCheck() const throw()
+    {
+      return m_env->ExceptionCheck();
+    }
+
+  /** Wrapped JNI function. */
   jthrowable ExceptionOccurred() const throw()
     {
       return m_env->ExceptionOccurred();
     }
 
   /** Wrapped JNI function. */
+  void ExceptionClear() const throw()
+    {
+      m_env->ExceptionClear();
+    }
+
+  /** Wrapped JNI function. */
   jclass FindClass(const char* name) const
     {
       jclass cls = m_env->FindClass(name);
@@ -259,6 +280,12 @@ public:
     }
 
   /** Wrapped JNI function. */
+  jboolean IsInstanceOf(jobject obj, jclass cls) const
+    {
+      return m_env->IsInstanceOf(obj, cls);
+    }
+
+  /** Wrapped JNI function. */
   jmethodID GetMethodID(jclass cls, const char* name, const char* sig) const
     {
       jmethodID mid = m_env->GetMethodID(cls, name, sig);
@@ -524,13 +551,13 @@ private:
 
   void throw_java_exception() const
     {
-      throw ::JavaHL::JavaException();
+      throw SignalExceptionThrown();
     }
 
   void check_java_exception() const
     {
       if (m_env->ExceptionCheck())
-        throw ::JavaHL::JavaException();
+        throw SignalExceptionThrown();
     }
 
   void throw_java_out_of_memory(const char* message) const;

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp?rev=1543993&r1=1543992&r2=1543993&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp Thu Nov 21 00:22:33 2013
@@ -27,7 +27,6 @@
 
 #include "jni_env.hpp"
 #include "jni_object.hpp"
-#include "jni_javahl_exception.hpp"
 
 namespace Java {
 
@@ -60,7 +59,7 @@ public:
   void raise() const
     {
       throw_java_exception();
-      throw ::JavaHL::JavaException();
+      throw SignalExceptionThrown();
     }
 
   /**
@@ -73,7 +72,7 @@ public:
   void raise(const char* message) const
     {
       throw_java_exception(message);
-      throw ::JavaHL::JavaException();
+      throw SignalExceptionThrown();
     }
 
   /**
@@ -168,6 +167,7 @@ private:
   static const char* const m_class_name;
 };
 
+
 /**
  * Generator class for exceptions of type @c java.lang.NullPointerException.
  *
@@ -207,6 +207,26 @@ private:
 };
 
 /**
+ * Generator class for exceptions of type
+ * @c java.lang.IndexOutOfBoundsException.
+ *
+ * @since New in 1.9.
+ */
+class IndexOutOfBoundsException : public Exception
+{
+public:
+  /**
+   * Constructs an exception generator object.
+   */
+  explicit IndexOutOfBoundsException(Env env)
+    : Exception(env, m_class_name)
+    {}
+
+private:
+  static const char* const m_class_name;
+};
+
+/**
  * Generator class for exceptions of type @c java.io.IOException.
  *
  * @since New in 1.9.

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_list.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_list.cpp?rev=1543993&r1=1543992&r2=1543993&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_list.cpp (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_list.cpp Thu Nov 21 00:22:33 2013
@@ -92,7 +92,7 @@ jint BaseMutableList::length() const
 {
   if (!m_mid_size)
     m_mid_size = m_env.GetMethodID(m_class, "size", "()I");
-  return m_env.CallIntMethod(m_jthis, m_mid_add);
+  return m_env.CallIntMethod(m_jthis, m_mid_size);
 }
 
 } // namespace Java

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_list.hpp
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_list.hpp?rev=1543993&r1=1543992&r2=1543993&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_list.hpp (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_list.hpp Thu Nov 21 00:22:33 2013
@@ -24,6 +24,7 @@
 #ifndef SVN_JAVAHL_JNIWRAPPER_LIST_HPP
 #define SVN_JAVAHL_JNIWRAPPER_LIST_HPP
 
+#include <algorithm>
 #include <vector>
 
 #include "jni_env.hpp"
@@ -63,16 +64,18 @@ protected:
 
   /**
    * Returns the object reference at @a index.
+   * @throw std::out_of_range if the index value is not valid.
    */
   jobject operator[](jint index) const
     {
       return m_contents[ovector::size_type(index)];
     }
 
+  const ovector m_contents;
+
 private:
   static const char* const m_class_name;
   static ovector convert_to_vector(Env env, jclass cls, jobject jlist);
-  const ovector m_contents;
 };
 
 /**
@@ -80,7 +83,7 @@ private:
  *
  * @since New in 1.9.
  */
-template <typename T>
+template <typename T, typename NativeT=jobject>
 class List : public BaseList
 {
 public:
@@ -94,11 +97,44 @@ public:
 
   /**
    * Returns a wrapper object for the object reference at @a index.
+   * @throw std::out_of_range if the index value is not valid.
    */
   T operator[](jint index) const
     {
-      return T(m_env, BaseList::operator[](index));
+      return T(m_env, NativeT(BaseList::operator[](index)));
+    }
+
+  /**
+   * Iterates over the items in the list, calling @a function for
+   * each item.
+   * @see std::for_each
+   */
+  template<typename F>
+  F for_each(F function) const
+    {
+      const FunctorAdapter<F> adapter(m_env, function);
+      std::for_each(m_contents.begin(), m_contents.end(), adapter);
+      return function;
     }
+
+private:
+  template<typename F>
+  struct FunctorAdapter
+  {
+    explicit FunctorAdapter(const Env& env, F& function)
+      : m_env(env),
+        m_function(function)
+      {}
+
+    void operator()(const jobject& obj) const
+      {
+        const T item(m_env, NativeT(obj));
+        m_function(item);
+      }
+
+    const Env& m_env;
+    F& m_function;
+  };
 };
 
 /**
@@ -148,6 +184,7 @@ protected:
 
   /**
    * Returns the object reference at @a index.
+   * @note Throws a Java exception if the index value is not valid.
    */
   jobject operator[](jint index) const;
 
@@ -164,7 +201,7 @@ private:
  *
  * @since New in 1.9.
  */
-template <typename T>
+template <typename T, typename NativeT=jobject>
 class MutableList : public BaseMutableList
 {
 public:
@@ -179,8 +216,8 @@ public:
    * Constructs and wraps an empty list of type @c java.util.ArrayList
    * with initial allocation size @a length.
    */
-  explicit MutableList(Env env, jint length = 0)
-    : BaseMutableList(env, length)
+  explicit MutableList(Env env, jint length_ = 0)
+    : BaseMutableList(env, length_)
     {}
 
   /**
@@ -193,10 +230,11 @@ public:
 
   /**
    * Returns a wrapper object for the object reference at @a index.
+   * @note Throws a Java exception if the index value is not valid.
    */
   T operator[](jint index) const
     {
-      return T(m_env, BaseMutableList::operator[](index));
+      return T(m_env, NativeT(BaseMutableList::operator[](index)));
     }
 };
 

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp?rev=1543993&r1=1543992&r2=1543993&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp Thu Nov 21 00:22:33 2013
@@ -24,10 +24,22 @@
 #ifndef SVN_JAVAHL_JNIWRAPPER_STACK_HPP
 #define SVN_JAVAHL_JNIWRAPPER_STACK_HPP
 
+#ifdef SVN_JAVAHL_DEBUG
+#  ifndef SVN_JAVAHL_ASSERT_EXCEPTION_THROWN
+#    include <cassert>
+#    define SVN_JAVAHL_ASSERT_EXCEPTION_THROWN(E) \
+       assert((E).ExceptionCheck())
+#  endif // SVN_JAVAHL_ASSERT_EXCEPTION_THROWN
+#else
+#  define SVN_JAVAHL_ASSERT_EXCEPTION_THROWN(E)
+# endif // SVN_JAVAHL_DEBUG
+
 #include "../JNIStackElement.h"
 #include "jni_env.hpp"
 #include "jni_exception.hpp"
 
+#include "svn_error.h"
+
 /**
  * Boilerplate for the native method implementation entry point.
  *
@@ -36,7 +48,7 @@
  * try/catch block of the function body.
  *
  * @param C The name of the Java class that declares this method.
- * @param M The (Java) name of the method
+ * @param M The (Java) name of the method.
  *
  * This macro expects two additional parameters to be available
  * (either as function arguments or local variables):
@@ -47,10 +59,22 @@
 #define SVN_JAVAHL_JNI_TRY(C, M)                                \
   ::JNIStackElement st_ac_ke_le_me_nt_(jenv, #C, #M, jthis);    \
   try
+
+/**
+ * Boilerplate for the native method implementation entry point.
+ *
+ * Initializes local variable named @a V as a pointer to an instance
+ * of the native-bound class @a C.
+ *
+ * @since New in 1.9.
+ */
+#define SVN_JAVAHL_GET_BOUND_OBJECT(C, V)               \
+  C* const V = C::get_self(::Java::Env(jenv), jthis)
+
 /**
  * Boilerplate for the native method implementation entry point.
  *
- * Like SVN_JAVAHL_JNI_TRY, but for static methods where the @c jthis
+ * Like #SVN_JAVAHL_JNI_TRY, but for static methods where the @c jthis
  * argument is not available.
  *
  * This macro expects two additional parameters to be available
@@ -63,6 +87,7 @@
   ::JNIStackElement st_ac_ke_le_me_nt_(jenv, #C, #M, jclazz);   \
   try
 
+
 /**
  * Boilerplate for the native method implementation exit point.
  *
@@ -70,35 +95,97 @@
  * macro to close the try/catch block of the function body and handle
  * any exceptions thrown by the method implementation.
  *
+ * This boilerplate variant converts C++ exceptions to the Java
+ * exception type @a X, but retains exceptions that are already in
+ * progress.
+ *
  * @since New in 1.9.
  */
-#define SVN_JAVAHL_JNI_CATCH                                            \
-  catch (const ::JavaHL::JavaException&)                                \
-    {}                                                                  \
+#define SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(X)                            \
+  catch (const ::Java::SignalExceptionThrown&)                          \
+    {                                                                   \
+      SVN_JAVAHL_ASSERT_EXCEPTION_THROWN(::Java::Env(jenv));            \
+    }                                                                   \
   catch (const ::std::exception& ex)                                    \
     {                                                                   \
-      ::Java::RuntimeException(::Java::Env(jenv))                       \
-        .throw_java_exception(ex.what());                               \
+      X(::Java::Env(jenv)).throw_java_exception(ex.what());             \
     }                                                                   \
   catch (...)                                                           \
     {                                                                   \
-      ::Java::RuntimeException(::Java::Env(jenv))                       \
+      X(::Java::Env(jenv))                                              \
         .throw_java_exception(_("Caught unknown C++ exception"));       \
     }
 
 /**
+ * Boilerplate for the native method implementation exit point.
+ *
+ * Invokes #SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION to throw a
+ * @c RuntimeException.
+ *
+ * @since New in 1.9.
+ */
+#define SVN_JAVAHL_JNI_CATCH                                            \
+  SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(::Java::RuntimeException)
+
+/**
  * Invocation wrapper for functions that return an @c svn_error_t *.
  *
+ * @param E A wrapped environment (@c Java::Env) instance.
+ * @param S The statement to execute in the checked context.
+ *
  * @since New in 1.9.
  */
-#define SVN_JAVAHL_CHECK(e)                             \
-  do {                                                  \
-    svn_error_t* javahl__err__temp = (e);               \
-    if (javahl__err__temp)                              \
-      {                                                 \
-        JNIUtil::handleSVNError(javahl__err__temp);     \
-        throw JavaHL::JavaException();                  \
-    }                                                   \
+#define SVN_JAVAHL_CHECK(E, S)                                          \
+  do {                                                                  \
+    svn_error_t* const ja_va_hl_err_te_mp_ = (S);                       \
+    if (ja_va_hl_err_te_mp_)                                            \
+      ::Java::handle_svn_error((E), ja_va_hl_err_te_mp_);               \
   } while(0)
 
+/**
+ * Invocation wrapper for calling Java methods that may throw an
+ * exception from within a native callback that is expected to return
+ * an @c svn_error_t*.
+ *
+ * @param E A wrapped environment (@c Java::Env) instance.
+ * @param C A Subversion or APR error code.
+ * @param S The statement to execute in the checked context.
+ *
+ * @since New in 1.9.
+ */
+#define SVN_JAVAHL_CATCH(E, C, S)                                       \
+  try                                                                   \
+    {                                                                   \
+      S;                                                                \
+    }                                                                   \
+  catch (const ::Java::SignalExceptionThrown&)                          \
+    {                                                                   \
+      SVN_JAVAHL_ASSERT_EXCEPTION_THROWN((E));                          \
+      return svn_error_create((C), NULL, _("Java exception"));          \
+    }                                                                   \
+  catch (const ::std::exception& ex)                                    \
+    {                                                                   \
+      const char* const msg = ex.what();                                \
+      ::Java::RuntimeException((E)).throw_java_exception(msg);          \
+      return svn_error_create((C), NULL, msg);                          \
+    }                                                                   \
+  catch (...)                                                           \
+    {                                                                   \
+      const char* const msg = _("Caught unknown C++ exception");        \
+      ::Java::RuntimeException((E)).throw_java_exception(msg);          \
+      return svn_error_create((C), NULL, msg);                          \
+    }
+
+namespace Java {
+
+/**
+ * Handle an error @a err returned from a native function and throws
+ * an appropriate Java exception.
+ *
+ * @since New in 1.9.
+ */
+void handle_svn_error(Env env, svn_error_t* err);
+
+} // namespace Java
+
 #endif // SVN_JAVAHL_JNIWRAPPER_STACK_HPP

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_PropLib.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_PropLib.cpp?rev=1543993&r1=1543992&r2=1543993&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_PropLib.cpp (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_PropLib.cpp Thu Nov 21 00:22:33 2013
@@ -199,6 +199,82 @@ inline bool operator!=(const svn_opt_rev
 {
   return !(a == b);
 }
+
+class UnparseFunctor
+{
+public:
+  explicit UnparseFunctor(std::ostringstream& buffer, bool old_format,
+                          SVN::Pool& iterpool)
+    : m_buffer(buffer),
+      m_old_format(old_format),
+      m_iterpool(iterpool)
+    {}
+
+  void operator()(const JavaHL::ExternalItem& item)
+    {
+      m_iterpool.clear();
+
+      const Java::Env env(item.get_env());
+      const Java::LocalFrame frame(env);
+
+      if (!m_old_format)
+        {
+          if (item.revision()->kind != svn_opt_revision_head
+              && *item.revision() != *item.peg_revision())
+            {
+              m_buffer << "-r"
+                       << FormatRevision(item.revision(), m_iterpool)
+                       << ' ';
+            }
+          if (item.peg_revision()->kind == svn_opt_revision_head)
+            m_buffer << item.url() << ' ';
+          else
+            {
+              m_buffer << item.url() << '@'
+                       << FormatRevision(item.peg_revision(), m_iterpool)
+                       << ' ';
+            }
+          m_buffer << item.target_dir() << '\n';
+        }
+      else
+        {
+          // Sanity check: old format does not support peg revisions
+          if (item.peg_revision()->kind != svn_opt_revision_head
+              && *item.revision() != *item.peg_revision())
+            {
+              JavaHL::SubversionException(env)
+                .raise(_("Clients older than Subversion 1.5"
+                         " do not support peg revision syntax"
+                         " in the svn:externals property"));
+            }
+
+          // Sanity check: old format does not support relative URLs
+          const std::string url = item.url();
+          if (   (url.size() >= 1 && (url[0] == '.' || url[0] == '/'))
+                 || (url.size() >= 2 && (url[0] == '^' && url[1] == '/')))
+            {
+              JavaHL::SubversionException(env)
+                .raise(_("Clients older than Subversion 1.5"
+                         " do not support relative URLs"
+                         " in the svn:externals property"));
+            }
+
+          m_buffer << item.target_dir() << ' ';
+          if (item.revision()->kind != svn_opt_revision_head)
+            {
+              m_buffer << "-r"
+                       << FormatRevision(item.revision(), m_iterpool)
+                       << ' ';
+            }
+          m_buffer << url << '\n';
+        }
+    }
+
+private:
+  std::ostringstream& m_buffer;
+  const bool m_old_format;
+  SVN::Pool& m_iterpool;
+};
 } // anoymous namespace
 
 
@@ -226,7 +302,8 @@ Java_org_apache_subversion_javahl_util_P
         svn_string_t* const description_contents =
           Java::ByteArray::Contents(description).get_string(pool);
 
-        SVN_JAVAHL_CHECK(svn_wc_parse_externals_description3(
+        SVN_JAVAHL_CHECK(env,
+                         svn_wc_parse_externals_description3(
                              &externals,
                              Java::String::Contents(parent_dir).c_str(),
                              description_contents->data,
@@ -274,71 +351,13 @@ Java_org_apache_subversion_javahl_util_P
       SVN::Pool iterpool;
 
       std::ostringstream buffer;
-      const jint items_length = items.length();
-      for (jint i = 0; i < items_length; ++i)
-        {
-          iterpool.clear();
-
-          const Java::LocalFrame frame(env);
-          const JavaHL::ExternalItem item(items[i]);
-
-          if (!jold_format)
-            {
-              if (item.revision()->kind != svn_opt_revision_head
-                  && *item.revision() != *item.peg_revision())
-                {
-                  buffer << "-r"
-                         << FormatRevision(item.revision(), iterpool)
-                         << ' ';
-                }
-              if (item.peg_revision()->kind == svn_opt_revision_head)
-                buffer << item.url() << ' ';
-              else
-                {
-                  buffer << item.url() << '@'
-                         << FormatRevision(item.peg_revision(), iterpool)
-                         << ' ';
-                }
-              buffer << item.target_dir() << '\n';
-            }
-          else
-            {
-              // Sanity check: old format does not support peg revisions
-              if (item.peg_revision()->kind != svn_opt_revision_head
-                  && *item.revision() != *item.peg_revision())
-                {
-                  JavaHL::SubversionException(env)
-                    .raise(_("Clients older than Subversion 1.5"
-                             " do not support peg revision syntax"
-                             " in the svn:externals property"));
-                }
-
-              // Sanity check: old format does not support relative URLs
-              const std::string url = item.url();
-              if (   (url.size() >= 1 && (url[0] == '.' || url[0] == '/'))
-                  || (url.size() >= 2 && (url[0] == '^' && url[1] == '/')))
-                {
-                  JavaHL::SubversionException(env)
-                    .raise(_("Clients older than Subversion 1.5"
-                             " do not support relative URLs"
-                             " in the svn:externals property"));
-                }
-
-              buffer << item.target_dir() << ' ';
-              if (item.revision()->kind != svn_opt_revision_head)
-                {
-                  buffer << "-r"
-                         << FormatRevision(item.revision(), iterpool)
-                         << ' ';
-                }
-              buffer << url << '\n';
-            }
-        }
+      items.for_each(UnparseFunctor(buffer, jold_format, iterpool));
+      const std::string description(buffer.str());
 
       // Validate the result. Even though we generated the string
       // ourselves, we did not validate the input paths and URLs.
-      const std::string description(buffer.str());
-      SVN_JAVAHL_CHECK(svn_wc_parse_externals_description3(
+      SVN_JAVAHL_CHECK(env,
+                       svn_wc_parse_externals_description3(
                            NULL,
                            Java::String::Contents(parent_dir).c_str(),
                            description.c_str(),
@@ -368,7 +387,8 @@ Java_org_apache_subversion_javahl_util_P
       SVN::Pool pool;
 
       const char* resolved_url;
-      SVN_JAVAHL_CHECK(svn_wc__resolve_relative_external_url(
+      SVN_JAVAHL_CHECK(env,
+                       svn_wc__resolve_relative_external_url(
                            &resolved_url,
                            item.get_external_item(pool),
                            Java::String::Contents(repos_root_url).c_str(),

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=1543993&r1=1543992&r2=1543993&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 Nov 21 00:22:33 2013
@@ -79,10 +79,10 @@ public class ClientException extends Nat
      * @param messageStack The whole stack of error messages
      * @since 1.9
      */
-    ClientException(String message, String source, int aprError,
-                    List<ErrorMessage> messageStack)
+    ClientException(String message, Throwable cause, String source,
+                    int aprError, List<ErrorMessage> messageStack)
     {
-        super(message, source, aprError);
+        super(message, source, cause, aprError);
         this.messageStack = messageStack;
     }
 
@@ -96,7 +96,7 @@ public class ClientException extends Nat
      */
     ClientException(String message, String source, int aprError)
     {
-        this(message, source, aprError, null);
+        this(message, null, source, aprError, null);
     }
 
     public List<ErrorMessage> getAllMessages()

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNClient.java
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNClient.java?rev=1543993&r1=1543992&r2=1543993&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNClient.java (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNClient.java Thu Nov 21 00:22:33 2013
@@ -308,6 +308,7 @@ public interface ISVNClient
      * @param handler   the commit message callback, may be <code>null</code>
      *                  if <code>destPath</code> is not a URL
      * @throws ClientException If the copy operation fails.
+     * @throws NullPointerException if the <code>sources</code> list is empty.
      */
     void copy(List<CopySource> sources, String destPath,
               boolean copyAsChild, boolean makeParents,

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeException.java
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeException.java?rev=1543993&r1=1543992&r2=1543993&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeException.java (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeException.java Thu Nov 21 00:22:33 2013
@@ -58,9 +58,10 @@ class NativeException extends Subversion
      * @param aprError Any associated APR error code for a wrapped
      * <code>svn_error_t</code>.
      */
-    NativeException(String message, String source, int aprError)
+    NativeException(String message, String source, Throwable cause,
+                    int aprError)
     {
-        super(message);
+        super(message, cause);
         this.source = source;
         this.aprError = aprError;
     }

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/SVNUtil.java
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/SVNUtil.java?rev=1543993&r1=1543992&r2=1543993&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/SVNUtil.java (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/SVNUtil.java Thu Nov 21 00:22:33 2013
@@ -29,7 +29,9 @@ import org.apache.subversion.javahl.util
 
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.Date;
 import java.util.List;
+import java.util.Map;
 
 public class SVNUtil
 {
@@ -398,6 +400,7 @@ public class SVNUtil
      *  <dd>relative to the scheme</dd>
      *  <dt><code>/</code></dt>
      *  <dd>relative to the server's hostname</dd>
+     * </dl>
      *<p>
      * The <code>../<code> and ^/ relative URLs may use <code>..<code>
      * to remove path elements up to the server root.
@@ -416,4 +419,236 @@ public class SVNUtil
         return propLib.resolveExternalsUrl(
                    external, reposRootUrl, parentDirUrl);
     }
+
+    //
+    // Newline translation and keyword expansion
+    //
+    private static final SubstLib substLib = new SubstLib();
+
+    /**
+     * Use the linefeed code point ('<code>\x0a</code>')
+     * for the newline separator.
+     * @see translateStream
+     * @see untranslateStream
+     */
+    public static final byte[] EOL_LF = substLib.EOL_LF;
+
+    /**
+     * Use the carraige-return code point ('<code>\x0d</code>')
+     * for the newline separator.
+     * @see translateStream
+     * @see untranslateStream
+     */
+    public static final byte[] EOL_CR = substLib.EOL_CR;
+
+    /**
+     * Use carriage-return/linefeed sequence ('<code>\x0d\x0a</code>')
+     * for the newline separator.
+     * @see translateStream
+     * @see untranslateStream
+     */
+    public static final byte[] EOL_CRLF = substLib.EOL_CRLF;
+
+
+    /**
+     * Build a dictionary of expanded keyword values, given the
+     * contents of a file's <code>svn:keywords</code> property, its
+     * revision, URL, the date it was committed on, the author of the
+     * commit and teh URL of the repository root.
+     *<p>
+     * Custom keywords defined in <code>svn:keywords</code> properties
+     * are expanded using the provided parameters and in accordance
+     * with the following format substitutions in the
+     * <code>keywordsValue</code>:
+     * <dl>
+     *   <dt><code>%a</dt></code>
+     * <dd>The author.</dd>
+     *   <dt><code>%b</dt></code>
+     * <dd>The basename of the URL.</dd>
+     *   <dt><code>%d</dt></code>
+     * <dd>Short format of the date.</dd>
+     *   <dt><code>%D</dt></code>
+     * <dd>Long format of the date.</dd>
+     *   <dt><code>%P</dt></code>
+     * <dd>The file's path, relative to the repository root URL.</dd>
+     *   <dt><code>%r</dt></code>
+     * <dd>The revision.</dd>
+     *   <dt><code>%R</dt></code>
+     * <dd>The URL to the root of the repository.</dd>
+     *   <dt><code>%u</dt></code>
+     * <dd>The URL of the file.</dd>
+     *   <dt><code>%_</dt></code>
+     * <dd>A space (keyword definitions cannot contain a literal space).</dd>
+     *   <dt><code>%%</dt></code>
+     * <dd>A literal '%'.</dd>
+     *   <dt><code>%H</dt></code>
+     * <dd>Equivalent to <code>%P%_%r%_%d%_%a</code>.</dd>
+     *   <dt><code>%I</dt></code>
+     * <dd>Equivalent to <code>%b%_%r%_%d%_%a</code>.</dd>
+     * </dl>
+     *<p>
+     * Custom keywords are defined by appending '=' to the keyword
+     * name, followed by a string containing any combination of the
+     * format substitutions.
+     *<p>
+     * Any of the <code>revision</code>, <code>url</code>,
+     * <code>reposRootUrl</code>, <code>date</code> and
+     * <code>author</code> parameters may be <code>null</code>, or
+     * {@link Revision#SVN_INVALID_REVNUM} for <code>revision</code>,
+     * to indicate that the information is not present. Each piece of
+     * information that is not present expands to the empty string
+     * wherever it appears in an expanded keyword value.  (This can
+     * result in multiple adjacent spaces in the expansion of a
+     * multi-valued keyword such as "<code>Id</code>".)
+     */
+    public static Map<String, byte[]> buildKeywords(byte[] keywordsValue,
+                                                    long revision,
+                                                    String url,
+                                                    String reposRootUrl,
+                                                    Date date,
+                                                    String author)
+        throws SubversionException, ClientException
+    {
+        return substLib.buildKeywords(keywordsValue, revision,
+                                      url, reposRootUrl, date, author);
+    }
+
+    /**
+     * Return a stream which performs end-of-line translation and
+     * keyword expansion when read from.
+     *<p>
+     * <b>Important:</b> Make sure you close the reurned stream to
+     * ensure all data are flushed and cleaned up (this will also
+     * close the provided stream and dispose the related netive
+     * object).
+     *<p>
+     * If <code>eolMarker</code> is not <code>null</code>, replace
+     * whatever any end-of-line sequences in the input with
+     * <code>eolMarker</code>.  If the input has an inconsistent line
+     * ending style, then:
+     * <ul>
+     *   <li>if <code>repairEol</code> is <code>false</code>, then a
+     *       subsequent read or other operation on the stream will
+     *       generate an error when the inconsistency is detected;</li>
+     *   <li>if <code>repaorEol</code> is <code>true</code>, convert any
+     *       line ending to <code>eolMarker</code>.<br/>
+     *       Recognized line endings are: "<code>\n</code>",
+     *       "<code>\r</code>", and "<code>\r\n</code>".</li>
+     * </ul>
+     *<p>
+     * Expand or contract keywords using the contents of
+     * <code>keywords</code> as the new values.  If
+     * <code>expandKeywords</code> is <code>true</code>, expand
+     * contracted keywords and re-expand expanded keywords; otherwise,
+     * contract expanded keywords and ignore contracted ones.
+     * Keywords not found in the dictionary are ignored (not
+     * contracted or expanded).  If the <code>keywords</code> itself
+     * is <code>null</code>, keyword substitution will be altogether
+     * ignored.
+     *<p>
+     * Detect only keywords that are no longer than
+     * <code>SVN_KEYWORD_MAX_LEN</code> bytes (currently: 255),
+     * including the delimiters and the keyword itself.
+     *<p>
+     * Recommendation: if <code>expandKeywords</code> is
+     * <code>false</code>, then you don't care about the keyword
+     * values, so just put <code>null</code> values into the
+     * <code>keywords</code> dictionary.
+     *<p>
+     * If the inner stream implements marking and seeking via
+     * {@link InputStream#mark} and {@link InputStream#reset}, the
+     * translated stream will too.
+     *
+     * @param source the source (untranslated) stream.
+     * @param eolMarker the byte sequence to use as the end-of-line marker;
+     *     must be one of {@link #EOL_LF}, {@link #EOL_CR}
+     *     or {@link #EOL_CRLF}.
+     * @param repairEol flag to repair end-of-lines; see above
+     * @param keywords the keyword dictionary; see {@link buildKeywords}
+     * @param expandKeywords flag to expand keywords
+     */
+    public static InputStream translateStream(InputStream source,
+                                              byte[] eolMarker,
+                                              boolean repairEol,
+                                              Map<String, byte[]> keywords,
+                                              boolean expandKeywords)
+        throws SubversionException, ClientException
+    {
+        return substLib.translateInputStream(
+                    source, eolMarker, repairEol,
+                    keywords, true, expandKeywords,
+                    null, Revision.SVN_INVALID_REVNUM,
+                    null, null, null, null);
+    }
+
+    /**
+     * Expand keywords and return a stream which performs end-of-line
+     * translation and keyword expansion when read from.
+     * @see buildKeywords
+     * @see translateStream(InputStream,byte[],boolean,Map,boolean)
+     */
+    public static InputStream translateStream(InputStream source,
+                                              byte[] eolMarker,
+                                              boolean repairEol,
+                                              boolean expandKeywords,
+                                              byte[] keywordsValue,
+                                              long revision,
+                                              String url,
+                                              String reposRootUrl,
+                                              Date date,
+                                              String author)
+        throws SubversionException, ClientException
+    {
+        return substLib.translateInputStream(
+                    source, eolMarker, repairEol,
+                    null, false, expandKeywords,
+                    keywordsValue, revision,
+                    url, reposRootUrl, date, author);
+    }
+
+    /**
+     * Return a stream which performs end-of-line translation and
+     * keyword expansion when written to. Behaves like
+     * {@link #translateStream(InputStream,byte[],boolean,Map,boolean)},
+     * except that it translates an <code>OutputStream</code> and never
+     * supports marking and seeking.
+     */
+    public static OutputStream translateStream(OutputStream destination,
+                                               byte[] eolMarker,
+                                               boolean repairEol,
+                                               Map<String, byte[]> keywords,
+                                               boolean expandKeywords)
+        throws SubversionException, ClientException
+    {
+        return substLib.translateOutputStream(
+                    destination, eolMarker, repairEol,
+                    keywords, true, expandKeywords,
+                    null, Revision.SVN_INVALID_REVNUM,
+                    null, null, null, null);
+    }
+
+    /**
+     * Expand keywords and return a stream which performs end-of-line
+     * translation and keyword expansion when written to.
+     * @see buildKeywords
+     * @see translateStream(OutputStream,byte[],boolean,Map,boolean)
+     */
+    public static OutputStream translateStream(OutputStream destination,
+                                               byte[] eolMarker,
+                                               boolean repairEol,
+                                               boolean expandKeywords,
+                                               byte[] keywordsValue,
+                                               long revision,
+                                               String url,
+                                               String reposRootUrl,
+                                               Date date,
+                                               String author)
+        throws SubversionException, ClientException
+    {
+        return substLib.translateOutputStream(
+                    destination, eolMarker, repairEol,
+                    null, false, expandKeywords,
+                    keywordsValue, revision,
+                    url, reposRootUrl, date, author);
+    }
 }

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/SubversionException.java
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/SubversionException.java?rev=1543993&r1=1543992&r2=1543993&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/SubversionException.java (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/SubversionException.java Thu Nov 21 00:22:33 2013
@@ -47,4 +47,16 @@ public class SubversionException extends
     {
         super(message);
     }
+
+    /**
+     * This constructor is only used by sub-classes and the native
+     * implementation.
+     *
+     * @param message A description of the problem.
+     * @param cause The root cause of the exception.
+     */
+    protected SubversionException(String message, Throwable cause)
+    {
+        super(message, cause);
+    }
 }

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/ExternalItem.java
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/ExternalItem.java?rev=1543993&r1=1543992&r2=1543993&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/ExternalItem.java (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/ExternalItem.java Thu Nov 21 00:22:33 2013
@@ -46,10 +46,10 @@ public class ExternalItem implements jav
      *
      * @param targetDir See {@link #getTargetDir}
      * @param url See {@link #getUrl}
--     * @param revision See {@link #getRevision};
--     *     <code>null</code> will be interpreted as <code>pegRevision</code>
--     * @param pegRevision See {@link #getPegRevision};
--     *     <code>null</code> will be interpreted as {@link Revision#HEAD}
+     * @param revision See {@link #getRevision};
+     *     <code>null</code> will be interpreted as <code>pegRevision</code>
+     * @param pegRevision See {@link #getPegRevision};
+     *     <code>null</code> will be interpreted as {@link Revision#HEAD}
      */
     public ExternalItem(String targetDir, String url,
                         Revision revision, Revision pegRevision)

Modified: subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/tests/org/apache/subversion/javahl/UtilTests.java
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/tests/org/apache/subversion/javahl/UtilTests.java?rev=1543993&r1=1543992&r2=1543993&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/tests/org/apache/subversion/javahl/UtilTests.java (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/bindings/javahl/tests/org/apache/subversion/javahl/UtilTests.java Thu Nov 21 00:22:33 2013
@@ -30,10 +30,15 @@ import org.apache.subversion.javahl.type
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.util.Arrays;
-import java.util.List;
 import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
 
 /**
  * Tests the JavaHL SVNUtil APIs.
@@ -298,11 +303,14 @@ public class UtilTests extends SVNTests
     private static List<ExternalItem> old_externals = null;
     static {
         try {
-            old_externals = new ArrayList<ExternalItem>(2);
+            old_externals = new ArrayList<ExternalItem>(3);
             old_externals.add(new ExternalItem("X", "http://server/repo/path",
                                                null, null));
             old_externals.add(new ExternalItem("Y", "http://server/repo/path",
                                                null, Revision.getInstance(42)));
+
+            old_externals.add(new ExternalItem("Z", "http://server/repo/path",
+                                               null, Revision.getInstance(new Date(0L))));
         } catch (SubversionException ex) {
             old_externals = null;
             throw new RuntimeException(ex);
@@ -311,7 +319,8 @@ public class UtilTests extends SVNTests
 
     private static final byte[] old_externals_propval =
         ("X http://server/repo/path\n" +
-         "Y -r42 http://server/repo/path\n").getBytes();
+         "Y -r42 http://server/repo/path\n" +
+         "Z -r{1970-01-01T00:00:00.000000Z} http://server/repo/path\n").getBytes();
 
     private static void compare_item_lists(List<ExternalItem> a,
                                            List<ExternalItem> b,
@@ -387,4 +396,181 @@ public class UtilTests extends SVNTests
                          new ExternalItem("x", "//a/b/c", null, null),
                          "http://a", "http://a/b"));
     }
+
+    public void testBuildKeywords() throws Throwable
+    {
+        final byte[] kwval = "TEST=%H%_%b%_%u".getBytes();
+
+        Map<String, byte[]> result;
+
+        result = SVNUtil.buildKeywords(kwval, Revision.SVN_INVALID_REVNUM,
+                                       null, null, null, null);
+        assertEquals("     ", new String(result.get("TEST")));
+
+        result = SVNUtil.buildKeywords(kwval, 42, "http://a/b/c",
+                                       "http://a", new Date(1), "X");
+        assertEquals("b/c 42 1970-01-01 00:00:00Z X c http://a/b/c",
+                     new String(result.get("TEST")));
+    }
+
+    public void testTranslateStream() throws Throwable
+    {
+        final byte[] keywordsValue = "Id TEST=%H%_%b%_%u".getBytes();
+        final byte[] contentsContracted = "$Id$\n$TEST$\n".getBytes();
+        final byte[] contentsExpanded =
+            ("$Id: c 42 1970-01-01 00:00:00Z X $\r" +
+             "$TEST: b/c 42 1970-01-01 00:00:00Z X c http://a/b/c $\r"
+             ) .getBytes();
+        final Map<String, byte[]> keywords =
+            SVNUtil.buildKeywords(keywordsValue, 42, "http://a/b/c",
+                                  "http://a", new Date(1), "X");
+        byte[] buffer = new byte[1024];
+
+        // InputStream; expand
+        InputStream testin = null;
+        try {
+            testin = SVNUtil.translateStream(
+                         new ByteArrayInputStream(contentsContracted),
+                         SVNUtil.EOL_CR, true, keywords, true);
+            final int size = testin.read(buffer);
+            testin.close();
+            testin = null;
+
+            assertEquals(new String(contentsExpanded),
+                         new String(buffer, 0, size));
+        } finally {
+            if (testin != null) {
+                testin.close();
+                testin = null;
+            }
+        }
+
+        try {
+            testin = SVNUtil.translateStream(
+                         new ByteArrayInputStream(contentsContracted),
+                         SVNUtil.EOL_CR, true, true,
+                         keywordsValue, 42, "http://a/b/c",
+                         "http://a", new Date(1), "X");
+            final int size = testin.read(buffer);
+            testin.close();
+            testin = null;
+
+            assertEquals(new String(contentsExpanded),
+                         new String(buffer, 0, size));
+        } finally {
+            if (testin != null) {
+                testin.close();
+                testin = null;
+            }
+        }
+
+        // InputStream; contract
+        try {
+            testin = SVNUtil.translateStream(
+                         new ByteArrayInputStream(contentsExpanded),
+                         SVNUtil.EOL_LF, true, keywords, false);
+            final int size = testin.read(buffer);
+            testin.close();
+            testin = null;
+
+            assertEquals(new String(contentsContracted),
+                         new String(buffer, 0, size));
+        } finally {
+            if (testin != null) {
+                testin.close();
+                testin = null;
+            }
+        }
+
+        try {
+            testin = SVNUtil.translateStream(
+                         new ByteArrayInputStream(contentsExpanded),
+                         SVNUtil.EOL_LF, true, false,
+                         keywordsValue, 42, "http://a/b/c",
+                         "http://a", new Date(1), "X");
+            final int size = testin.read(buffer);
+            testin.close();
+            testin = null;
+
+            assertEquals(new String(contentsContracted),
+                         new String(buffer, 0, size));
+        } finally {
+            if (testin != null) {
+                testin.close();
+                testin = null;
+            }
+        }
+
+
+        // OutputStream; expand
+        OutputStream testout = null;
+        try {
+            ByteArrayOutputStream result = new ByteArrayOutputStream();
+            testout = SVNUtil.translateStream(
+                         result, SVNUtil.EOL_CR, true, keywords, true);
+            testout.write(contentsContracted);
+            testout.close();
+            testout = null;
+
+            assertEquals(new String(contentsExpanded), result.toString());
+        } finally {
+            if (testout != null) {
+                testout.close();
+                testout = null;
+            }
+        }
+
+        try {
+            ByteArrayOutputStream result = new ByteArrayOutputStream();
+            testout = SVNUtil.translateStream(
+                         result, SVNUtil.EOL_CR, true, true,
+                         keywordsValue, 42, "http://a/b/c",
+                         "http://a", new Date(1), "X");
+            testout.write(contentsContracted);
+            testout.close();
+            testout = null;
+
+            assertEquals(new String(contentsExpanded), result.toString());
+        } finally {
+            if (testout != null) {
+                testout.close();
+                testout = null;
+            }
+        }
+
+        // OutputStream; contract
+        try {
+            ByteArrayOutputStream result = new ByteArrayOutputStream();
+            testout = SVNUtil.translateStream(
+                         result, SVNUtil.EOL_LF, true, keywords, false);
+            testout.write(contentsExpanded);
+            testout.close();
+            testout = null;
+
+            assertEquals(new String(contentsContracted), result.toString());
+        } finally {
+            if (testout != null) {
+                testout.close();
+                testout = null;
+            }
+        }
+
+        try {
+            ByteArrayOutputStream result = new ByteArrayOutputStream();
+            testout = SVNUtil.translateStream(
+                         result, SVNUtil.EOL_LF, true, false,
+                         keywordsValue, 42, "http://a/b/c",
+                         "http://a", new Date(1), "X");
+            testout.write(contentsExpanded);
+            testout.close();
+            testout = null;
+
+            assertEquals(new String(contentsContracted), result.toString());
+        } finally {
+            if (testout != null) {
+                testout.close();
+                testout = null;
+            }
+        }
+    }
 }