summaryrefslogtreecommitdiff
path: root/subversion/bindings/javahl/native
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/bindings/javahl/native')
-rw-r--r--subversion/bindings/javahl/native/AuthnCallback.cpp365
-rw-r--r--subversion/bindings/javahl/native/AuthnCallback.hpp376
-rw-r--r--subversion/bindings/javahl/native/BlameCallback.cpp10
-rw-r--r--subversion/bindings/javahl/native/ChangelistCallback.cpp2
-rw-r--r--subversion/bindings/javahl/native/ClientContext.cpp289
-rw-r--r--subversion/bindings/javahl/native/ClientContext.h34
-rw-r--r--subversion/bindings/javahl/native/CommitCallback.cpp24
-rw-r--r--subversion/bindings/javahl/native/CommitCallback.h17
-rw-r--r--subversion/bindings/javahl/native/CommitEditor.cpp618
-rw-r--r--subversion/bindings/javahl/native/CommitEditor.h132
-rw-r--r--subversion/bindings/javahl/native/CommitMessage.cpp8
-rw-r--r--subversion/bindings/javahl/native/CopySources.cpp12
-rw-r--r--subversion/bindings/javahl/native/CreateJ.cpp563
-rw-r--r--subversion/bindings/javahl/native/CreateJ.h27
-rw-r--r--subversion/bindings/javahl/native/Credential.cpp85
-rw-r--r--subversion/bindings/javahl/native/Credential.hpp126
-rw-r--r--subversion/bindings/javahl/native/DiffOptions.cpp24
-rw-r--r--subversion/bindings/javahl/native/DiffOptions.h2
-rw-r--r--subversion/bindings/javahl/native/DiffSummaryReceiver.cpp15
-rw-r--r--subversion/bindings/javahl/native/EditorCallbacks.cpp124
-rw-r--r--subversion/bindings/javahl/native/EditorCallbacks.hpp328
-rw-r--r--subversion/bindings/javahl/native/EditorProxy.cpp566
-rw-r--r--subversion/bindings/javahl/native/EditorProxy.h153
-rw-r--r--subversion/bindings/javahl/native/EnumMapper.cpp106
-rw-r--r--subversion/bindings/javahl/native/EnumMapper.h6
-rw-r--r--subversion/bindings/javahl/native/ExternalItem.cpp176
-rw-r--r--subversion/bindings/javahl/native/ExternalItem.hpp139
-rw-r--r--subversion/bindings/javahl/native/GlobalConfig.h38
-rw-r--r--subversion/bindings/javahl/native/ImportFilterCallback.cpp5
-rw-r--r--subversion/bindings/javahl/native/InfoCallback.cpp8
-rw-r--r--subversion/bindings/javahl/native/InfoCallback.h2
-rw-r--r--subversion/bindings/javahl/native/InputStream.cpp6
-rw-r--r--subversion/bindings/javahl/native/Iterator.cpp115
-rw-r--r--subversion/bindings/javahl/native/Iterator.h64
-rw-r--r--subversion/bindings/javahl/native/JNIByteArray.cpp30
-rw-r--r--subversion/bindings/javahl/native/JNIByteArray.h9
-rw-r--r--subversion/bindings/javahl/native/JNICriticalSection.h2
-rw-r--r--subversion/bindings/javahl/native/JNIStackElement.cpp12
-rw-r--r--subversion/bindings/javahl/native/JNIStackElement.h2
-rw-r--r--subversion/bindings/javahl/native/JNIStringHolder.h1
-rw-r--r--subversion/bindings/javahl/native/JNIThreadData.cpp169
-rw-r--r--subversion/bindings/javahl/native/JNIUtil.cpp799
-rw-r--r--subversion/bindings/javahl/native/JNIUtil.h118
-rw-r--r--subversion/bindings/javahl/native/ListCallback.cpp60
-rw-r--r--subversion/bindings/javahl/native/LockTokenTable.cpp114
-rw-r--r--subversion/bindings/javahl/native/LockTokenTable.h50
-rw-r--r--subversion/bindings/javahl/native/LogMessageCallback.cpp12
-rw-r--r--subversion/bindings/javahl/native/MessageReceiver.cpp2
-rw-r--r--subversion/bindings/javahl/native/NativeStream.cpp381
-rw-r--r--subversion/bindings/javahl/native/NativeStream.hpp211
-rw-r--r--subversion/bindings/javahl/native/OperationContext.cpp657
-rw-r--r--subversion/bindings/javahl/native/OperationContext.h128
-rw-r--r--subversion/bindings/javahl/native/PatchCallback.cpp4
-rw-r--r--subversion/bindings/javahl/native/PatchCallback.h2
-rw-r--r--subversion/bindings/javahl/native/Path.cpp81
-rw-r--r--subversion/bindings/javahl/native/Path.h145
-rw-r--r--subversion/bindings/javahl/native/Prompter.cpp912
-rw-r--r--subversion/bindings/javahl/native/Prompter.h274
-rw-r--r--subversion/bindings/javahl/native/PropertyTable.cpp (renamed from subversion/bindings/javahl/native/RevpropTable.cpp)52
-rw-r--r--subversion/bindings/javahl/native/PropertyTable.h (renamed from subversion/bindings/javahl/native/RevpropTable.h)15
-rw-r--r--subversion/bindings/javahl/native/ProplistCallback.cpp12
-rw-r--r--subversion/bindings/javahl/native/RemoteSession.cpp1322
-rw-r--r--subversion/bindings/javahl/native/RemoteSession.h128
-rw-r--r--subversion/bindings/javahl/native/RemoteSessionContext.cpp125
-rw-r--r--subversion/bindings/javahl/native/RemoteSessionContext.h51
-rw-r--r--subversion/bindings/javahl/native/ReposFreezeAction.cpp2
-rw-r--r--subversion/bindings/javahl/native/ReposNotifyCallback.cpp4
-rw-r--r--subversion/bindings/javahl/native/ReposVerifyCallback.cpp88
-rw-r--r--subversion/bindings/javahl/native/ReposVerifyCallback.h75
-rw-r--r--subversion/bindings/javahl/native/Revision.cpp12
-rw-r--r--subversion/bindings/javahl/native/Revision.h1
-rw-r--r--subversion/bindings/javahl/native/RevisionRange.cpp132
-rw-r--r--subversion/bindings/javahl/native/RevisionRange.h8
-rw-r--r--subversion/bindings/javahl/native/RevisionRangeList.cpp128
-rw-r--r--subversion/bindings/javahl/native/RevisionRangeList.h (renamed from subversion/bindings/javahl/native/JNIThreadData.h)53
-rw-r--r--subversion/bindings/javahl/native/SVNBase.cpp26
-rw-r--r--subversion/bindings/javahl/native/SVNBase.h5
-rw-r--r--subversion/bindings/javahl/native/SVNClient.cpp440
-rw-r--r--subversion/bindings/javahl/native/SVNClient.h70
-rw-r--r--subversion/bindings/javahl/native/SVNRepos.cpp94
-rw-r--r--subversion/bindings/javahl/native/SVNRepos.h9
-rw-r--r--subversion/bindings/javahl/native/StateReporter.cpp191
-rw-r--r--subversion/bindings/javahl/native/StateReporter.h74
-rw-r--r--subversion/bindings/javahl/native/StatusCallback.cpp9
-rw-r--r--subversion/bindings/javahl/native/SubversionException.cpp (renamed from subversion/bindings/javahl/native/libsvnjavahl.la.c)21
-rw-r--r--subversion/bindings/javahl/native/SubversionException.hpp54
-rw-r--r--subversion/bindings/javahl/native/Utility.cpp96
-rw-r--r--subversion/bindings/javahl/native/Utility.hpp90
-rw-r--r--subversion/bindings/javahl/native/VersionExtended.cpp8
-rw-r--r--subversion/bindings/javahl/native/VersionExtended.h2
-rw-r--r--subversion/bindings/javahl/native/deprecated.cpp60
-rw-r--r--subversion/bindings/javahl/native/jniwrapper/jni_array.hpp267
-rw-r--r--subversion/bindings/javahl/native/jniwrapper/jni_base.cpp382
-rw-r--r--subversion/bindings/javahl/native/jniwrapper/jni_channel.cpp257
-rw-r--r--subversion/bindings/javahl/native/jniwrapper/jni_channel.hpp223
-rw-r--r--subversion/bindings/javahl/native/jniwrapper/jni_class_cache.cpp326
-rw-r--r--subversion/bindings/javahl/native/jniwrapper/jni_env.hpp682
-rw-r--r--subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp364
-rw-r--r--subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp90
-rw-r--r--subversion/bindings/javahl/native/jniwrapper/jni_io_stream.cpp294
-rw-r--r--subversion/bindings/javahl/native/jniwrapper/jni_io_stream.hpp281
-rw-r--r--subversion/bindings/javahl/native/jniwrapper/jni_iterator.cpp63
-rw-r--r--subversion/bindings/javahl/native/jniwrapper/jni_iterator.hpp91
-rw-r--r--subversion/bindings/javahl/native/jniwrapper/jni_list.cpp85
-rw-r--r--subversion/bindings/javahl/native/jniwrapper/jni_list.hpp307
-rw-r--r--subversion/bindings/javahl/native/jniwrapper/jni_object.hpp285
-rw-r--r--subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp220
-rw-r--r--subversion/bindings/javahl/native/jniwrapper/jni_string.hpp251
-rw-r--r--subversion/bindings/javahl/native/jniwrapper/jni_string_map.cpp110
-rw-r--r--subversion/bindings/javahl/native/jniwrapper/jni_string_map.hpp384
-rw-r--r--subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp5
-rw-r--r--subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNClient.cpp248
-rw-r--r--subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNRepos.cpp39
-rw-r--r--subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_CommitEditor.cpp205
-rw-r--r--subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteFactory.cpp61
-rw-r--r--subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteSession.cpp359
-rw-r--r--subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_StateReporter.cpp117
-rw-r--r--subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RevisionRangeList.cpp58
-rw-r--r--subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RuntimeVersion.cpp69
-rw-r--r--subversion/bindings/javahl/native/org_apache_subversion_javahl_types_Version.cpp13
-rw-r--r--subversion/bindings/javahl/native/org_apache_subversion_javahl_types_VersionExtended.cpp16
-rw-r--r--subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigImpl_Category.cpp314
-rw-r--r--subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigLib.cpp492
-rw-r--r--subversion/bindings/javahl/native/org_apache_subversion_javahl_util_DiffLib.cpp206
-rw-r--r--subversion/bindings/javahl/native/org_apache_subversion_javahl_util_PropLib.cpp381
-rw-r--r--subversion/bindings/javahl/native/org_apache_subversion_javahl_util_SubstLib.cpp219
-rw-r--r--subversion/bindings/javahl/native/org_apache_subversion_javahl_util_TunnelChannel.cpp174
127 files changed, 18025 insertions, 2110 deletions
diff --git a/subversion/bindings/javahl/native/AuthnCallback.cpp b/subversion/bindings/javahl/native/AuthnCallback.cpp
new file mode 100644
index 0000000..3a25e04
--- /dev/null
+++ b/subversion/bindings/javahl/native/AuthnCallback.cpp
@@ -0,0 +1,365 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#include "svn_base64.h"
+#include "svn_x509.h"
+
+#include "jniwrapper/jni_stack.hpp"
+#include "jniwrapper/jni_exception.hpp"
+#include "jniwrapper/jni_string.hpp"
+#include "jniwrapper/jni_array.hpp"
+#include "jniwrapper/jni_list.hpp"
+
+#include "AuthnCallback.hpp"
+
+#include "svn_private_config.h"
+
+namespace JavaHL {
+
+// Class JavaHL::AuthnCallback
+const char* const AuthnCallback::m_class_name =
+ JAVAHL_CLASS("/callback/AuthnCallback");
+
+AuthnCallback::ClassImpl::ClassImpl(::Java::Env env, jclass cls)
+ : ::Java::Object::ClassImpl(env, cls),
+ m_mid_username_prompt(
+ env.GetMethodID(cls, "usernamePrompt",
+ "(Ljava/lang/String;Z)"
+ JAVAHL_ARG("/callback/AuthnCallback")
+ "$UsernameResult;")),
+ m_mid_user_password_prompt(
+ env.GetMethodID(cls, "userPasswordPrompt",
+ "(Ljava/lang/String;Ljava/lang/String;Z)"
+ JAVAHL_ARG("/callback/AuthnCallback")
+ "$UserPasswordResult;")),
+ m_mid_ssl_server_trust_prompt(
+ env.GetMethodID(cls, "sslServerTrustPrompt",
+ "(Ljava/lang/String;"
+ JAVAHL_ARG("/callback/AuthnCallback")
+ "$SSLServerCertFailures;"
+ JAVAHL_ARG("/callback/AuthnCallback")
+ "$SSLServerCertInfo;"
+ "Z)"
+ JAVAHL_ARG("/callback/AuthnCallback")
+ "$SSLServerTrustResult;")),
+ m_mid_ssl_client_cert_prompt(
+ env.GetMethodID(cls, "sslClientCertPrompt",
+ "(Ljava/lang/String;Z)"
+ JAVAHL_ARG("/callback/AuthnCallback")
+ "$SSLClientCertResult;")),
+ m_mid_ssl_client_cert_passphrase_prompt(
+ env.GetMethodID(cls, "sslClientCertPassphrasePrompt",
+ "(Ljava/lang/String;Z)"
+ JAVAHL_ARG("/callback/AuthnCallback")
+ "$SSLClientCertPassphraseResult;")),
+ m_mid_allow_store_plaintext_password(
+ env.GetMethodID(cls, "allowStorePlaintextPassword",
+ "(Ljava/lang/String;)Z")),
+ m_mid_allow_store_plaintext_passphrase(
+ env.GetMethodID(cls, "allowStorePlaintextPassphrase",
+ "(Ljava/lang/String;)Z"))
+{}
+
+AuthnCallback::ClassImpl::~ClassImpl() {}
+
+jobject AuthnCallback::username_prompt(const ::Java::String& realm,
+ bool may_save)
+{
+ return m_env.CallObjectMethod(m_jthis, impl().m_mid_username_prompt,
+ realm.get(), jboolean(may_save));
+}
+
+
+jobject AuthnCallback::user_password_prompt(const ::Java::String& realm,
+ const ::Java::String& username,
+ bool may_save)
+{
+ return m_env.CallObjectMethod(m_jthis, impl().m_mid_user_password_prompt,
+ realm.get(), username.get(),
+ jboolean(may_save));
+}
+
+jobject AuthnCallback::ssl_server_trust_prompt(
+ const ::Java::String& realm,
+ const SSLServerCertFailures& failures,
+ const SSLServerCertInfo& info,
+ bool may_save)
+{
+ return m_env.CallObjectMethod(m_jthis, impl().m_mid_ssl_server_trust_prompt,
+ realm.get(), failures.get(), info.get(),
+ jboolean(may_save));
+}
+
+jobject AuthnCallback::ssl_client_cert_prompt(const ::Java::String&
+ realm, bool may_save)
+{
+ return m_env.CallObjectMethod(m_jthis, impl().m_mid_ssl_client_cert_prompt,
+ realm.get(), jboolean(may_save));
+}
+
+jobject AuthnCallback::ssl_client_cert_passphrase_prompt(
+ const ::Java::String& realm,
+ bool may_save)
+{
+ return m_env.CallObjectMethod(m_jthis,
+ impl().m_mid_ssl_client_cert_passphrase_prompt,
+ realm.get(), jboolean(may_save));
+}
+
+bool AuthnCallback::allow_store_plaintext_password(const ::Java::String& realm)
+{
+ return m_env.CallBooleanMethod(m_jthis,
+ impl().m_mid_allow_store_plaintext_password,
+ realm.get());
+}
+
+bool AuthnCallback::allow_store_plaintext_passphrase(const ::Java::String& realm)
+{
+ return m_env.CallBooleanMethod(m_jthis,
+ impl().m_mid_allow_store_plaintext_passphrase,
+ realm.get());
+}
+
+
+// Class JavaHL::AuthnCallback::AuthnResult
+const char* const AuthnCallback::AuthnResult::m_class_name =
+ JAVAHL_CLASS("/callback/AuthnCallback$AuthnResult");
+
+AuthnCallback::AuthnResult::ClassImpl::ClassImpl(::Java::Env env, jclass cls)
+ : ::Java::Object::ClassImpl(env, cls),
+ m_fid_save(env.GetFieldID(cls, "save", "Z")),
+ m_fid_trust(env.GetFieldID(cls, "trust", "Z")),
+ m_fid_identity(env.GetFieldID(cls, "identity", "Ljava/lang/String;")),
+ m_fid_secret(env.GetFieldID(cls, "secret", "Ljava/lang/String;"))
+{}
+
+AuthnCallback::AuthnResult::ClassImpl::~ClassImpl() {}
+
+// Class JavaHL::AuthnCallback::SSLServerCertFailures
+const char* const AuthnCallback::SSLServerCertFailures::m_class_name =
+ JAVAHL_CLASS("/callback/AuthnCallback$SSLServerCertFailures");
+
+AuthnCallback::SSLServerCertFailures::ClassImpl::ClassImpl(
+ ::Java::Env env, jclass cls)
+ : ::Java::Object::ClassImpl(env, cls),
+ m_mid_ctor(env.GetMethodID(cls, "<init>", "(I)V"))
+{}
+
+AuthnCallback::SSLServerCertFailures::ClassImpl::~ClassImpl() {}
+
+AuthnCallback::SSLServerCertFailures::SSLServerCertFailures(
+ ::Java::Env env, jint failures)
+ : ::Java::Object(env,
+ ::Java::ClassCache::get_authn_ssl_server_cert_failures(env))
+{
+ set_this(env.NewObject(get_class(), impl().m_mid_ctor, failures));
+}
+
+
+// Class JavaHL::AuthnCallback::SSLServerCertInfo
+const char* const AuthnCallback::SSLServerCertInfo::m_class_name =
+ JAVAHL_CLASS("/callback/AuthnCallback$SSLServerCertInfo");
+
+AuthnCallback::SSLServerCertInfo::ClassImpl::ClassImpl(
+ ::Java::Env env, jclass cls)
+ : ::Java::Object::ClassImpl(env, cls),
+ m_mid_ctor(env.GetMethodID(cls, "<init>",
+ "(Ljava/lang/String;"
+ "Ljava/lang/String;JJ[B"
+ "Ljava/util/List;"
+ "Ljava/lang/String;)V"))
+{}
+
+AuthnCallback::SSLServerCertInfo::ClassImpl::~ClassImpl() {}
+
+AuthnCallback::SSLServerCertInfo::SSLServerCertInfo(
+ ::Java::Env env, const char* ascii_cert)
+ : ::Java::Object(env,
+ ::Java::ClassCache::get_authn_ssl_server_cert_info(env))
+{
+ SVN::Pool pool;
+
+ /* Convert header-less PEM to DER by undoing base64 encoding. */
+ const svn_string_t cert_string = { ascii_cert, strlen(ascii_cert) };
+ const svn_string_t* der = svn_base64_decode_string(&cert_string,
+ pool.getPool());
+
+ svn_x509_certinfo_t *certinfo;
+ SVN_JAVAHL_CHECK(env, svn_x509_parse_cert(&certinfo, der->data, der->len,
+ pool.getPool(), pool.getPool()));
+
+ const ::Java::String subject(
+ env, svn_x509_certinfo_get_subject(certinfo, pool.getPool()));
+ const ::Java::String issuer(
+ env, svn_x509_certinfo_get_issuer(certinfo, pool.getPool()));
+ const ::Java::String cert(env, ascii_cert);
+ const jlong valid_from =
+ (jlong(svn_x509_certinfo_get_valid_from(certinfo)) + 500) / 1000;
+ const jlong valid_to =
+ (jlong(svn_x509_certinfo_get_valid_to(certinfo)) + 500) / 1000;
+
+ const svn_checksum_t* digest = svn_x509_certinfo_get_digest(certinfo);
+ jsize digest_size;
+ switch (digest->kind)
+ {
+ case svn_checksum_sha1:
+ digest_size = 160 / 8;
+ break;
+
+ case svn_checksum_md5:
+ digest_size = 128 / 8;
+ break;
+
+ default:
+ digest_size = 0; // Initialize this to avoid compiler warnings
+ ::Java::IllegalArgumentException(env).raise(
+ _("Unknown certificate digest type"));
+ }
+ const ::Java::ByteArray fingerprint(env, digest->digest, digest_size);
+
+ jobject jhostnames = NULL;
+ const apr_array_header_t* hostnames =
+ svn_x509_certinfo_get_hostnames(certinfo);
+ if (hostnames)
+ {
+ ::Java::List< ::Java::String> hn(env, hostnames->nelts);
+ for (int i = 0; i < hostnames->nelts; ++i)
+ hn.add(::Java::String(env, APR_ARRAY_IDX(hostnames, i, const char*)));
+ jhostnames = hn.get();
+ }
+
+ set_this(env.NewObject(get_class(), impl().m_mid_ctor,
+ subject.get(), issuer.get(),
+ valid_from, valid_to,
+ fingerprint.get(),
+ jhostnames,
+ cert.get()));
+}
+
+
+// Class JavaHL::UserPasswordCallback
+const char* const UserPasswordCallback::m_class_name =
+ JAVAHL_CLASS("/callback/UserPasswordCallback");
+
+UserPasswordCallback::ClassImpl::ClassImpl(::Java::Env env, jclass cls)
+ : ::Java::Object::ClassImpl(env, cls),
+ m_mid_ask_trust_ssl_server(
+ env.GetMethodID(cls, "askTrustSSLServer",
+ "(Ljava/lang/String;Z)I")),
+ m_mid_prompt_2arg(
+ env.GetMethodID(cls, "prompt",
+ "(Ljava/lang/String;Ljava/lang/String;)Z")),
+ m_mid_ask_yes_no(
+ env.GetMethodID(cls, "askYesNo",
+ "(Ljava/lang/String;Ljava/lang/String;Z)Z")),
+ m_mid_ask_question_3arg(
+ env.GetMethodID(cls, "askQuestion",
+ "(Ljava/lang/String;Ljava/lang/String;Z)"
+ "Ljava/lang/String;")),
+ m_mid_get_username(
+ env.GetMethodID(cls, "getUsername",
+ "()Ljava/lang/String;")),
+ m_mid_get_password(
+ env.GetMethodID(cls, "getPassword",
+ "()Ljava/lang/String;")),
+ m_mid_prompt(
+ env.GetMethodID(cls, "prompt",
+ "(Ljava/lang/String;Ljava/lang/String;Z)Z")),
+ m_mid_ask_question(
+ env.GetMethodID(cls, "askQuestion",
+ "(Ljava/lang/String;Ljava/lang/String;ZZ)"
+ "Ljava/lang/String;")),
+ m_mid_user_allowed_save(
+ env.GetMethodID(cls, "userAllowedSave", "()Z"))
+{}
+
+UserPasswordCallback::ClassImpl::~ClassImpl() {}
+
+jint UserPasswordCallback::ask_trust_ssl_server(const ::Java::String& info,
+ bool allow_permanently)
+{
+ return m_env.CallIntMethod(m_jthis, impl().m_mid_ask_trust_ssl_server,
+ info.get(), jboolean(allow_permanently));
+}
+
+bool UserPasswordCallback::prompt(const ::Java::String& realm,
+ const ::Java::String& username)
+{
+ return m_env.CallBooleanMethod(m_jthis, impl().m_mid_prompt_2arg,
+ realm.get(), username.get());
+}
+
+bool UserPasswordCallback::ask_yes_no(const ::Java::String& realm,
+ const ::Java::String& question,
+ bool yes_is_default)
+{
+ return m_env.CallBooleanMethod(m_jthis, impl().m_mid_ask_yes_no,
+ realm.get(), question.get(),
+ jboolean(yes_is_default));
+}
+
+jstring UserPasswordCallback::ask_question(const ::Java::String& realm,
+ const ::Java::String& question,
+ bool show_answer)
+{
+ return jstring(m_env.CallObjectMethod(m_jthis,
+ impl().m_mid_ask_question_3arg,
+ realm.get(), question.get(),
+ jboolean(show_answer)));
+}
+
+jstring UserPasswordCallback::get_username()
+{
+ return jstring(m_env.CallObjectMethod(m_jthis, impl().m_mid_get_username));
+}
+
+jstring UserPasswordCallback::get_password()
+{
+ return jstring(m_env.CallObjectMethod(m_jthis, impl().m_mid_get_password));
+}
+
+bool UserPasswordCallback::prompt(const ::Java::String& realm,
+ const ::Java::String& username,
+ bool may_save)
+{
+ return m_env.CallBooleanMethod(m_jthis, impl().m_mid_prompt,
+ realm.get(), username.get(),
+ jboolean(may_save));
+}
+
+jstring UserPasswordCallback::ask_question(const ::Java::String& realm,
+ const ::Java::String& question,
+ bool show_answer, bool may_save)
+{
+ return jstring(m_env.CallObjectMethod(m_jthis, impl().m_mid_ask_question,
+ realm.get(), question.get(),
+ jboolean(show_answer),
+ jboolean(may_save)));
+}
+
+bool UserPasswordCallback::user_allowed_save()
+{
+ return m_env.CallBooleanMethod(m_jthis, impl().m_mid_user_allowed_save);
+}
+
+} // namespace JavaHL
diff --git a/subversion/bindings/javahl/native/AuthnCallback.hpp b/subversion/bindings/javahl/native/AuthnCallback.hpp
new file mode 100644
index 0000000..f30d004
--- /dev/null
+++ b/subversion/bindings/javahl/native/AuthnCallback.hpp
@@ -0,0 +1,376 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#ifndef SVN_JAVAHL_AUTHN_CALLBACK_HPP
+#define SVN_JAVAHL_AUTHN_CALLBACK_HPP
+
+#include "svn_auth.h"
+
+#include "Pool.h"
+
+#include "jniwrapper/jni_object.hpp"
+#include "jniwrapper/jni_string.hpp"
+
+namespace JavaHL {
+
+/**
+ * Object wrapper for @c org.apache.subversion.javahl.callback.AuthnCallback.
+ *
+ * @since New in 1.9.
+ */
+class AuthnCallback : public ::Java::Object
+{
+public:
+ /**
+ * Object wrapper for @c ...AuthnCallback$AuthnResult.
+ */
+ class AuthnResult : public ::Java::Object
+ {
+ public:
+ /**
+ * Constructs a wrapper around @a jthis.
+ * The constructor does not verify the class of the wrapped object.
+ */
+ explicit AuthnResult(::Java::Env env, jobject jthis)
+ : ::Java::Object(env, ::Java::ClassCache::get_authn_result(env), jthis)
+ {}
+
+ bool save() const
+ {
+ return (0 != m_env.GetBooleanField(m_jthis, impl().m_fid_save));
+ }
+
+ bool trust() const
+ {
+ return (0 != m_env.GetBooleanField(m_jthis, impl().m_fid_trust));
+ }
+
+ jstring identity() const
+ {
+ return jstring(m_env.GetObjectField(m_jthis, impl().m_fid_identity));
+ }
+
+ jstring secret() const
+ {
+ return jstring(m_env.GetObjectField(m_jthis, impl().m_fid_secret));
+ }
+
+ private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ::Java::ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(::Java::Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const ::Java::FieldID m_fid_save;
+ const ::Java::FieldID m_fid_trust;
+ const ::Java::FieldID m_fid_identity;
+ const ::Java::FieldID m_fid_secret;
+ };
+
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+
+ friend class ::Java::ClassCacheImpl;
+ static const char* const m_class_name;
+ };
+
+
+ /**
+ * Object wrapper for @c ...AuthnCallback$SSLServerCertFailures.
+ */
+ class SSLServerCertFailures : public ::Java::Object
+ {
+ public:
+ /**
+ * Creates and initializes a wrapped object;
+ * @a failures is a set of flags.
+ */
+ explicit SSLServerCertFailures(::Java::Env env, jint failures);
+
+ private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ::Java::ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(::Java::Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const ::Java::MethodID m_mid_ctor;
+ };
+
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+
+ friend class ::Java::ClassCacheImpl;
+ static const char* const m_class_name;
+ };
+
+
+ /**
+ * Object wrapper for @c ...AuthnCallback$SSLServerCertInfo.
+ */
+ class SSLServerCertInfo : public ::Java::Object
+ {
+ public:
+ /**
+ * Creates and initializes a wrapped object;
+ */
+ explicit SSLServerCertInfo(::Java::Env env, const char* ascii_cert);
+
+ private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ::Java::ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(::Java::Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const ::Java::MethodID m_mid_ctor;
+ };
+
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+
+ friend class ::Java::ClassCacheImpl;
+ static const char* const m_class_name;
+ };
+
+
+ /**
+ * Constructs a wrapper around @a jthis.
+ * The constructor does not verify the class of the wrapped object.
+ */
+ explicit AuthnCallback(::Java::Env env, jobject jthis)
+ : ::Java::Object(env, ::Java::ClassCache::get_authn_cb(env), jthis)
+ {}
+
+ /**
+ * Invokes the Java method AuthnCallback.usernamePrompt().
+ */
+ jobject username_prompt(const ::Java::String& realm, bool may_save);
+
+
+ /**
+ * Invokes the Java method AuthnCallback.userPasswordPrompt().
+ */
+ jobject user_password_prompt(const ::Java::String& realm,
+ const ::Java::String& username,
+ bool may_save);
+
+ /**
+ * Invokes the Java method AuthnCallback.sslServerTrustPrompt().
+ */
+ jobject ssl_server_trust_prompt(const ::Java::String& realm,
+ const SSLServerCertFailures& failures,
+ const SSLServerCertInfo& info,
+ bool may_save);
+
+ /**
+ * Invokes the Java method AuthnCallback.sslClientCertPrompt().
+ */
+ jobject ssl_client_cert_prompt(const ::Java::String& realm, bool may_save);
+
+ /**
+ * Invokes the Java method AuthnCallback.sslClientCertPassphrasePrompt().
+ */
+ jobject ssl_client_cert_passphrase_prompt(const ::Java::String& realm,
+ bool may_save);
+
+ /**
+ * Invokes the Java method AuthnCallback.allowStorePlaintextPassword().
+ */
+ bool allow_store_plaintext_password(const ::Java::String& realm);
+
+ /**
+ * Invokes the Java method AuthnCallback.allowStorePlaintextPassphrase().
+ */
+ bool allow_store_plaintext_passphrase(const ::Java::String& realm);
+
+private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ::Java::ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(::Java::Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const ::Java::MethodID m_mid_username_prompt;
+ const ::Java::MethodID m_mid_user_password_prompt;
+ const ::Java::MethodID m_mid_ssl_server_trust_prompt;
+ const ::Java::MethodID m_mid_ssl_client_cert_prompt;
+ const ::Java::MethodID m_mid_ssl_client_cert_passphrase_prompt;
+ const ::Java::MethodID m_mid_allow_store_plaintext_password;
+ const ::Java::MethodID m_mid_allow_store_plaintext_passphrase;
+ };
+
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+
+ friend class ::Java::ClassCacheImpl;
+ static const char* const m_class_name;
+};
+
+
+/**
+ * Object wrapper for the deprecated interface
+ * @c org.apache.subversion.javahl.callback.UserPasswordCallback.
+ *
+ * @since New in 1.9.
+ */
+class UserPasswordCallback : public ::Java::Object
+{
+public:
+ /**
+ * Constructs a wrapper around @a jthis.
+ * The constructor does not verify the class of the wrapped object.
+ */
+ explicit UserPasswordCallback(::Java::Env env, jobject jthis)
+ : ::Java::Object(env, ::Java::ClassCache::get_user_passwd_cb(env), jthis)
+ {}
+
+ /**
+ * Invokes the Java method UserPasswordCallback.askTrustSSLServer().
+ */
+ jint ask_trust_ssl_server(const ::Java::String& info,
+ bool allow_permanently);
+
+ /**
+ * Invokes the Java method UserPasswordCallback.prompt().
+ */
+ bool prompt(const ::Java::String& realm,
+ const ::Java::String& username);
+
+ /**
+ * Invokes the Java method UserPasswordCallback.askYesNo().
+ */
+ bool ask_yes_no(const ::Java::String& realm,
+ const ::Java::String& question,
+ bool yes_is_default);
+
+ /**
+ * Invokes the Java method UserPasswordCallback.askQuestion().
+ */
+ jstring ask_question(const ::Java::String& realm,
+ const ::Java::String& question,
+ bool show_answer);
+
+ /**
+ * Invokes the Java method UserPasswordCallback.getUsername().
+ */
+ jstring get_username();
+
+ /**
+ * Invokes the Java method UserPasswordCallback.getPassword().
+ */
+ jstring get_password();
+
+ /**
+ * Invokes the Java method UserPasswordCallback.prompt().
+ */
+ bool prompt(const ::Java::String& realm,
+ const ::Java::String& username,
+ bool may_save);
+
+ /**
+ * Invokes the Java method UserPasswordCallback.askQuestion().
+ */
+ jstring ask_question(const ::Java::String& realm,
+ const ::Java::String& question,
+ bool show_answer, bool may_save);
+
+ /**
+ * Invokes the Java method UserPasswordCallback.userAllowedSave().
+ */
+ bool user_allowed_save();
+
+private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ::Java::ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(::Java::Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const ::Java::MethodID m_mid_ask_trust_ssl_server;
+ const ::Java::MethodID m_mid_prompt_2arg;
+ const ::Java::MethodID m_mid_ask_yes_no;
+ const ::Java::MethodID m_mid_ask_question_3arg;
+ const ::Java::MethodID m_mid_get_username;
+ const ::Java::MethodID m_mid_get_password;
+ const ::Java::MethodID m_mid_prompt;
+ const ::Java::MethodID m_mid_ask_question;
+ const ::Java::MethodID m_mid_user_allowed_save;
+ };
+
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+
+ friend class ::Java::ClassCacheImpl;
+ static const char* const m_class_name;
+};
+
+} // namespace JavaHL
+
+#endif // SVN_JAVAHL_AUTHN_CALLBACK_HPP
diff --git a/subversion/bindings/javahl/native/BlameCallback.cpp b/subversion/bindings/javahl/native/BlameCallback.cpp
index 5973707..10a8ff7 100644
--- a/subversion/bindings/javahl/native/BlameCallback.cpp
+++ b/subversion/bindings/javahl/native/BlameCallback.cpp
@@ -92,7 +92,7 @@ BlameCallback::singleLine(svn_revnum_t start_revnum, svn_revnum_t end_revnum,
static jmethodID mid = 0;
if (mid == 0)
{
- jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/BlameCallback");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/BlameCallback"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN(SVN_NO_ERROR);
@@ -104,14 +104,14 @@ BlameCallback::singleLine(svn_revnum_t start_revnum, svn_revnum_t end_revnum,
}
// convert the parameters to their Java relatives
- jobject jrevProps = CreateJ::PropertyMap(revProps);
+ jobject jrevProps = CreateJ::PropertyMap(revProps, pool);
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN(SVN_NO_ERROR);
jobject jmergedRevProps = NULL;
if (mergedRevProps != NULL)
{
- jmergedRevProps = CreateJ::PropertyMap(mergedRevProps);
+ jmergedRevProps = CreateJ::PropertyMap(mergedRevProps, pool);
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN(SVN_NO_ERROR);
}
@@ -128,8 +128,6 @@ BlameCallback::singleLine(svn_revnum_t start_revnum, svn_revnum_t end_revnum,
env->CallVoidMethod(m_callback, mid, (jlong)line_no, (jlong)revision,
jrevProps, (jlong)mergedRevision, jmergedRevProps,
jmergedPath, jline, (jboolean)localChange);
- // No need to check for an exception here, because we return anyway.
- env->PopLocalFrame(NULL);
- return SVN_NO_ERROR;
+ POP_AND_RETURN_EXCEPTION_AS_SVNERROR();
}
diff --git a/subversion/bindings/javahl/native/ChangelistCallback.cpp b/subversion/bindings/javahl/native/ChangelistCallback.cpp
index d04ba32..621de3d 100644
--- a/subversion/bindings/javahl/native/ChangelistCallback.cpp
+++ b/subversion/bindings/javahl/native/ChangelistCallback.cpp
@@ -78,7 +78,7 @@ ChangelistCallback::doChangelist(const char *path, const char *changelist,
// it can be cached.
if (mid == 0)
{
- jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/ChangelistCallback");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/ChangelistCallback"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NOTHING();
diff --git a/subversion/bindings/javahl/native/ClientContext.cpp b/subversion/bindings/javahl/native/ClientContext.cpp
index f1babc9..c9f327b 100644
--- a/subversion/bindings/javahl/native/ClientContext.cpp
+++ b/subversion/bindings/javahl/native/ClientContext.cpp
@@ -26,7 +26,6 @@
#include "svn_client.h"
#include "private/svn_wc_private.h"
-#include "svn_private_config.h"
#include "ClientContext.h"
#include "JNIUtil.h"
@@ -37,39 +36,13 @@
#include "EnumMapper.h"
#include "CommitMessage.h"
+#include "svn_private_config.h"
ClientContext::ClientContext(jobject jsvnclient, SVN::Pool &pool)
- : m_prompter(NULL),
- m_cancelOperation(false)
+ : OperationContext(pool)
{
- JNIEnv *env = JNIUtil::getEnv();
-
- /* Grab a global reference to the Java object embedded in the parent Java
- object. */
static jfieldID ctxFieldID = 0;
- if (ctxFieldID == 0)
- {
- jclass clazz = env->GetObjectClass(jsvnclient);
- if (JNIUtil::isJavaExceptionThrown())
- return;
-
- ctxFieldID = env->GetFieldID(clazz, "clientContext",
- "L"JAVA_PACKAGE"/SVNClient$ClientContext;");
- if (JNIUtil::isJavaExceptionThrown() || ctxFieldID == 0)
- return;
-
- env->DeleteLocalRef(clazz);
- }
-
- jobject jctx = env->GetObjectField(jsvnclient, ctxFieldID);
- if (JNIUtil::isJavaExceptionThrown())
- return;
-
- m_jctx = env->NewGlobalRef(jctx);
- if (JNIUtil::isJavaExceptionThrown())
- return;
-
- env->DeleteLocalRef(jctx);
+ attachJavaObject(jsvnclient, JAVAHL_ARG("/SVNClient$ClientContext;"), "clientContext", &ctxFieldID);
SVN_JNI_ERR(svn_client_create_context2(&m_context, NULL,
pool.getPool()),
@@ -96,18 +69,36 @@ ClientContext::ClientContext(jobject jsvnclient, SVN::Pool &pool)
m_context->conflict_func2 = resolve;
m_context->conflict_baton2 = m_jctx;
- m_context->client_name = "javahl";
- m_pool = &pool;
+ m_context->client_name = getClientName();
+
+ if (m_jtunnelcb)
+ {
+ m_context->check_tunnel_func = checkTunnel;
+ m_context->open_tunnel_func = openTunnel;
+ m_context->tunnel_baton = m_jtunnelcb;
+ }
}
ClientContext::~ClientContext()
{
- delete m_prompter;
-
- JNIEnv *env = JNIUtil::getEnv();
- env->DeleteGlobalRef(m_jctx);
}
+void ClientContext::setTunnelCallback(jobject jtunnelcb)
+{
+ OperationContext::setTunnelCallback(jtunnelcb);
+ if (m_jtunnelcb)
+ {
+ m_context->check_tunnel_func = checkTunnel;
+ m_context->open_tunnel_func = openTunnel;
+ m_context->tunnel_baton = m_jtunnelcb;
+ }
+ else
+ {
+ m_context->check_tunnel_func = NULL;
+ m_context->open_tunnel_func = NULL;
+ m_context->tunnel_baton = NULL;
+ }
+}
/* Helper function to make sure that we don't keep dangling pointers in ctx.
Note that this function might be called multiple times if getContext()
@@ -140,7 +131,6 @@ svn_client_ctx_t *
ClientContext::getContext(CommitMessage *message, SVN::Pool &in_pool)
{
apr_pool_t *pool = in_pool.getPool();
- svn_auth_baton_t *ab;
svn_client_ctx_t *ctx = m_context;
/* Make a temporary copy of ctx to restore at pool cleanup to avoid
@@ -157,122 +147,17 @@ ClientContext::getContext(CommitMessage *message, SVN::Pool &in_pool)
apr_pool_cleanup_register(in_pool.getPool(), bt, clear_ctx_ptrs,
clear_ctx_ptrs);
-
if (!ctx->config)
{
- const char *configDir = m_configDir.c_str();
- if (m_configDir.empty())
- configDir = NULL;
- SVN_JNI_ERR(svn_config_get_config(&(ctx->config), configDir,
- m_pool->getPool()),
- NULL);
+ apr_hash_t * configData = getConfigData();
+ ctx->config = configData;
bt->backup->config = ctx->config;
}
- svn_config_t *config =
- reinterpret_cast<svn_config_t *>(apr_hash_get(ctx->config,
- SVN_CONFIG_CATEGORY_CONFIG,
- APR_HASH_KEY_STRING));
-
-
- /* The whole list of registered providers */
- apr_array_header_t *providers;
-
- /* Populate the registered providers with the platform-specific providers */
- SVN_JNI_ERR(svn_auth_get_platform_specific_client_providers(&providers,
- config,
- pool),
- NULL);
-
- /* Use the prompter (if available) to prompt for password and cert
- * caching. */
- svn_auth_plaintext_prompt_func_t plaintext_prompt_func = NULL;
- void *plaintext_prompt_baton = NULL;
- svn_auth_plaintext_passphrase_prompt_func_t plaintext_passphrase_prompt_func;
- void *plaintext_passphrase_prompt_baton = NULL;
-
- if (m_prompter != NULL)
- {
- plaintext_prompt_func = Prompter::plaintext_prompt;
- plaintext_prompt_baton = m_prompter;
- plaintext_passphrase_prompt_func = Prompter::plaintext_passphrase_prompt;
- plaintext_passphrase_prompt_baton = m_prompter;
- }
-
- /* The main disk-caching auth providers, for both
- * 'username/password' creds and 'username' creds. */
- svn_auth_provider_object_t *provider;
-
- svn_auth_get_simple_provider2(&provider, plaintext_prompt_func,
- plaintext_prompt_baton, pool);
- APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
-
- svn_auth_get_username_provider(&provider, pool);
- APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
-
- /* The server-cert, client-cert, and client-cert-password providers. */
- SVN_JNI_ERR(svn_auth_get_platform_specific_provider(&provider,
- "windows",
- "ssl_server_trust",
- pool),
- NULL);
-
- if (provider)
- APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
-
- svn_auth_get_ssl_server_trust_file_provider(&provider, pool);
- APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
- svn_auth_get_ssl_client_cert_file_provider(&provider, pool);
- APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
- svn_auth_get_ssl_client_cert_pw_file_provider2(&provider,
- plaintext_passphrase_prompt_func,
- plaintext_passphrase_prompt_baton, pool);
- APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
-
- if (m_prompter != NULL)
- {
- /* Two basic prompt providers: username/password, and just username.*/
- provider = m_prompter->getProviderSimple(in_pool);
-
- APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
-
- provider = m_prompter->getProviderUsername(in_pool);
- APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
-
- /* Three ssl prompt providers, for server-certs, client-certs,
- * and client-cert-passphrases. */
- provider = m_prompter->getProviderServerSSLTrust(in_pool);
- APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
-
- provider = m_prompter->getProviderClientSSL(in_pool);
- APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
-
- provider = m_prompter->getProviderClientSSLPassword(in_pool);
- APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
- }
- /* Build an authentication baton to give to libsvn_client. */
- svn_auth_open(&ab, providers, pool);
-
- /* Place any default --username or --password credentials into the
- * auth_baton's run-time parameter hash. ### Same with --no-auth-cache? */
- if (!m_userName.empty())
- svn_auth_set_parameter(ab, SVN_AUTH_PARAM_DEFAULT_USERNAME,
- apr_pstrdup(in_pool.getPool(),
- m_userName.c_str()));
- if (!m_passWord.empty())
- svn_auth_set_parameter(ab, SVN_AUTH_PARAM_DEFAULT_PASSWORD,
- apr_pstrdup(in_pool.getPool(),
- m_passWord.c_str()));
- /* Store where to retrieve authentication data? */
- if (!m_configDir.empty())
- svn_auth_set_parameter(ab, SVN_AUTH_PARAM_CONFIG_DIR,
- apr_pstrdup(in_pool.getPool(),
- m_configDir.c_str()));
-
- ctx->auth_baton = ab;
+ ctx->auth_baton = getAuthBaton(in_pool);
ctx->log_msg_baton3 = message;
- m_cancelOperation = false;
+ resetCancelRequest();
SVN_JNI_ERR(svn_wc_context_create(&ctx->wc_ctx, NULL,
in_pool.getPool(), in_pool.getPool()),
@@ -282,60 +167,6 @@ ClientContext::getContext(CommitMessage *message, SVN::Pool &in_pool)
}
void
-ClientContext::username(const char *pi_username)
-{
- m_userName = (pi_username == NULL ? "" : pi_username);
-}
-
-void
-ClientContext::password(const char *pi_password)
-{
- m_passWord = (pi_password == NULL ? "" : pi_password);
-}
-
-void
-ClientContext::setPrompt(Prompter *prompter)
-{
- delete m_prompter;
- m_prompter = prompter;
-}
-
-void
-ClientContext::setConfigDirectory(const char *configDir)
-{
- // A change to the config directory may necessitate creation of
- // the config templates.
- SVN::Pool requestPool;
- SVN_JNI_ERR(svn_config_ensure(configDir, requestPool.getPool()), );
-
- m_configDir = (configDir == NULL ? "" : configDir);
- m_context->config = NULL;
-}
-
-const char *
-ClientContext::getConfigDirectory() const
-{
- return m_configDir.c_str();
-}
-
-void
-ClientContext::cancelOperation()
-{
- m_cancelOperation = true;
-}
-
-svn_error_t *
-ClientContext::checkCancel(void *cancelBaton)
-{
- ClientContext *that = static_cast<ClientContext *>(cancelBaton);
- if (that->m_cancelOperation)
- return svn_error_create(SVN_ERR_CANCELLED, NULL,
- _("Operation cancelled"));
- else
- return SVN_NO_ERROR;
-}
-
-void
ClientContext::notify(void *baton,
const svn_wc_notify_t *notify,
apr_pool_t *pool)
@@ -351,7 +182,7 @@ ClientContext::notify(void *baton,
return;
mid = env->GetMethodID(clazz, "onNotify",
- "(L"JAVA_PACKAGE"/ClientNotifyInformation;)V");
+ "(" JAVAHL_ARG("/ClientNotifyInformation;") ")V");
if (JNIUtil::isJavaExceptionThrown() || mid == 0)
return;
@@ -369,54 +200,6 @@ ClientContext::notify(void *baton,
env->DeleteLocalRef(jInfo);
}
-void
-ClientContext::progress(apr_off_t progressVal, apr_off_t total,
- void *baton, apr_pool_t *pool)
-{
- jobject jctx = (jobject) baton;
- JNIEnv *env = JNIUtil::getEnv();
-
- // Create a local frame for our references
- env->PushLocalFrame(LOCAL_FRAME_SIZE);
- if (JNIUtil::isJavaExceptionThrown())
- return;
-
- static jmethodID mid = 0;
- if (mid == 0)
- {
- jclass clazz = env->GetObjectClass(jctx);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NOTHING();
-
- mid = env->GetMethodID(clazz, "onProgress",
- "(L"JAVA_PACKAGE"/ProgressEvent;)V");
- if (JNIUtil::isJavaExceptionThrown() || mid == 0)
- POP_AND_RETURN_NOTHING();
- }
-
- static jmethodID midCT = 0;
- jclass clazz = env->FindClass(JAVA_PACKAGE"/ProgressEvent");
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NOTHING();
-
- if (midCT == 0)
- {
- midCT = env->GetMethodID(clazz, "<init>", "(JJ)V");
- if (JNIUtil::isJavaExceptionThrown() || midCT == 0)
- POP_AND_RETURN_NOTHING();
- }
-
- // Call the Java method.
- jobject jevent = env->NewObject(clazz, midCT,
- (jlong) progressVal, (jlong) total);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NOTHING();
-
- env->CallVoidMethod(jctx, mid, jevent);
-
- POP_AND_RETURN_NOTHING();
-}
-
svn_error_t *
ClientContext::resolve(svn_wc_conflict_result_t **result,
const svn_wc_conflict_description2_t *desc,
@@ -440,8 +223,8 @@ ClientContext::resolve(svn_wc_conflict_result_t **result,
POP_AND_RETURN(SVN_NO_ERROR);
mid = env->GetMethodID(clazz, "resolve",
- "(L"JAVA_PACKAGE"/ConflictDescriptor;)"
- "L"JAVA_PACKAGE"/ConflictResult;");
+ "(" JAVAHL_ARG("/ConflictDescriptor;") ")"
+ JAVAHL_ARG("/ConflictResult;"));
if (JNIUtil::isJavaExceptionThrown() || mid == 0)
POP_AND_RETURN(SVN_NO_ERROR);
}
@@ -493,7 +276,7 @@ ClientContext::javaResultToC(jobject jresult, apr_pool_t *pool)
jclass clazz = NULL;
if (getChoice == 0 || getMergedPath == 0)
{
- clazz = env->FindClass(JAVA_PACKAGE "/ConflictResult");
+ clazz = env->FindClass(JAVAHL_CLASS("/ConflictResult"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
}
@@ -501,7 +284,7 @@ ClientContext::javaResultToC(jobject jresult, apr_pool_t *pool)
if (getChoice == 0)
{
getChoice = env->GetMethodID(clazz, "getChoice",
- "()L"JAVA_PACKAGE"/ConflictResult$Choice;");
+ "()" JAVAHL_ARG("/ConflictResult$Choice;"));
if (JNIUtil::isJavaExceptionThrown() || getChoice == 0)
POP_AND_RETURN_NULL;
}
diff --git a/subversion/bindings/javahl/native/ClientContext.h b/subversion/bindings/javahl/native/ClientContext.h
index de3ece1..55ae9ff 100644
--- a/subversion/bindings/javahl/native/ClientContext.h
+++ b/subversion/bindings/javahl/native/ClientContext.h
@@ -29,6 +29,8 @@
#include <string>
+#include "OperationContext.h"
+
#include "svn_types.h"
#include "svn_client.h"
@@ -36,7 +38,6 @@
#include "Pool.h"
#include "JNIStringHolder.h"
-class Prompter;
class CommitMessage;
/**
@@ -44,25 +45,14 @@ class CommitMessage;
* and implements the functions read & close of svn_stream_t.
*
*/
-class ClientContext
+class ClientContext : public OperationContext
{
private:
svn_client_ctx_t *m_context;
- const SVN::Pool *m_pool;
- jobject m_jctx;
-
- std::string m_userName;
- std::string m_passWord;
- std::string m_configDir;
-
- Prompter *m_prompter;
- bool m_cancelOperation;
protected:
static void notify(void *baton, const svn_wc_notify_t *notify,
apr_pool_t *pool);
- static void progress(apr_off_t progressVal, apr_off_t total,
- void *baton, apr_pool_t *pool);
static svn_error_t *resolve(svn_wc_conflict_result_t **result,
const svn_wc_conflict_description2_t *desc,
void *baton,
@@ -73,24 +63,10 @@ class ClientContext
public:
ClientContext(jobject jsvnclient, SVN::Pool &pool);
- ~ClientContext();
-
- static svn_error_t *checkCancel(void *cancelBaton);
+ virtual ~ClientContext();
+ virtual void setTunnelCallback(jobject jtunnelcb);
svn_client_ctx_t *getContext(CommitMessage *message, SVN::Pool &in_pool);
-
- void username(const char *pi_username);
- void password(const char *pi_password);
- void setPrompt(Prompter *prompter);
- void cancelOperation();
- const char *getConfigDirectory() const;
-
- /**
- * Set the configuration directory, taking the usual steps to
- * ensure that Subversion's config file templates exist in the
- * specified location.
- */
- void setConfigDirectory(const char *configDir);
};
#endif // CLIENTCONTEXT_H
diff --git a/subversion/bindings/javahl/native/CommitCallback.cpp b/subversion/bindings/javahl/native/CommitCallback.cpp
index 2ffe326..2139312 100644
--- a/subversion/bindings/javahl/native/CommitCallback.cpp
+++ b/subversion/bindings/javahl/native/CommitCallback.cpp
@@ -37,9 +37,8 @@
* @param jcallback the Java callback object.
*/
CommitCallback::CommitCallback(jobject jcallback)
-{
- m_callback = jcallback;
-}
+ : m_callback(jcallback)
+{}
/**
* Destroy a CommitCallback object
@@ -81,13 +80,13 @@ CommitCallback::commitInfo(const svn_commit_info_t *commit_info,
static jmethodID sm_mid = 0;
if (sm_mid == 0)
{
- jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/CommitCallback");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/CommitCallback"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN(SVN_NO_ERROR);
sm_mid = env->GetMethodID(clazz,
"commitInfo",
- "(L"JAVA_PACKAGE"/CommitInfo;)V");
+ "(" JAVAHL_ARG("/CommitInfo;") ")V");
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN(SVN_NO_ERROR);
}
@@ -97,8 +96,17 @@ CommitCallback::commitInfo(const svn_commit_info_t *commit_info,
POP_AND_RETURN(SVN_NO_ERROR);
env->CallVoidMethod(m_callback, sm_mid, jcommitInfo);
- // No need to check for an exception here, because we return anyway.
- env->PopLocalFrame(NULL);
- return SVN_NO_ERROR;
+ POP_AND_RETURN_EXCEPTION_AS_SVNERROR();
+}
+
+
+PersistentCommitCallback::PersistentCommitCallback(jobject jcallback)
+ : CommitCallback(JNIUtil::getEnv()->NewGlobalRef(jcallback))
+{}
+
+PersistentCommitCallback::~PersistentCommitCallback()
+{
+ if (m_callback)
+ JNIUtil::getEnv()->DeleteGlobalRef(m_callback);
}
diff --git a/subversion/bindings/javahl/native/CommitCallback.h b/subversion/bindings/javahl/native/CommitCallback.h
index d08219f..0c4fe0f 100644
--- a/subversion/bindings/javahl/native/CommitCallback.h
+++ b/subversion/bindings/javahl/native/CommitCallback.h
@@ -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
diff --git a/subversion/bindings/javahl/native/CommitEditor.cpp b/subversion/bindings/javahl/native/CommitEditor.cpp
new file mode 100644
index 0000000..0a550d8
--- /dev/null
+++ b/subversion/bindings/javahl/native/CommitEditor.cpp
@@ -0,0 +1,618 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file CommitEditor.cpp
+ * @brief Implementation of the class CommitEditor
+ */
+
+#include "CommitEditor.h"
+#include "EnumMapper.h"
+#include "InputStream.h"
+#include "Iterator.h"
+#include "JNIByteArray.h"
+#include "LockTokenTable.h"
+#include "PropertyTable.h"
+#include "RemoteSession.h"
+
+#include <apr_tables.h>
+#include "svn_checksum.h"
+#include "private/svn_editor.h"
+#include "private/svn_ra_private.h"
+#include "svn_private_config.h"
+
+#include "EditorCallbacks.hpp"
+#include "jniwrapper/jni_string.hpp"
+#include "jniwrapper/jni_stack.hpp"
+
+CommitEditor*
+CommitEditor::getCppObject(jobject jthis)
+{
+ static jfieldID fid = 0;
+ jlong cppAddr = SVNBase::findCppAddrForJObject(
+ jthis, &fid, JAVAHL_CLASS("/remote/CommitEditor"));
+ return (cppAddr == 0 ? NULL : reinterpret_cast<CommitEditor*>(cppAddr));
+}
+
+jlong
+CommitEditor::createInstance(jobject jsession,
+ jobject jrevprops,
+ jobject jcommit_callback,
+ jobject jlock_tokens,
+ jboolean jkeep_locks,
+ jobject jget_base_cb,
+ jobject jget_props_cb,
+ jobject jget_kind_cb)
+{
+ RemoteSession* session = RemoteSession::getCppObject(jsession);
+ CPPADDR_NULL_PTR(session, 0);
+
+ CommitEditor* editor = new CommitEditor(session,
+ jrevprops, jcommit_callback,
+ jlock_tokens, jkeep_locks,
+ jget_base_cb, jget_props_cb,
+ jget_kind_cb);
+ if (JNIUtil::isJavaExceptionThrown())
+ {
+ delete editor;
+ return 0;
+ }
+ return editor->getCppAddr();
+}
+
+CommitEditor::CommitEditor(RemoteSession* session,
+ jobject jrevprops, jobject jcommit_callback,
+ jobject jlock_tokens, jboolean jkeep_locks,
+ jobject jget_base_cb, jobject jget_props_cb,
+ jobject jget_kind_cb)
+
+ : m_valid(false),
+ m_callback(jcommit_callback),
+ m_session(session),
+ m_editor(NULL),
+ m_get_base_cb(Java::Env(), jget_base_cb),
+ m_get_props_cb(Java::Env(), jget_props_cb),
+ m_get_kind_cb(Java::Env(), jget_kind_cb),
+ m_callback_session(NULL),
+ m_callback_session_url(NULL),
+ m_callback_session_uuid(NULL)
+{
+ // Store the repository root identity from the current session as we
+ // may need it to open another session in get_copysrc_kind_cb.
+ SVN_JNI_ERR(svn_ra_get_repos_root2(session->m_session,
+ &m_callback_session_url,
+ pool.getPool()),);
+ SVN_JNI_ERR(svn_ra_get_uuid2(session->m_session,
+ &m_callback_session_uuid,
+ pool.getPool()),);
+
+ PropertyTable revprops(jrevprops, true, true);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ LockTokenTable lock_tokens(jlock_tokens);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ SVN::Pool subPool(pool);
+ SVN_JNI_ERR(svn_ra__get_commit_ev2(
+ &m_editor,
+ session->m_session,
+ revprops.hash(subPool),
+ m_callback.callback, &m_callback,
+ lock_tokens.hash(subPool, true),
+ bool(jkeep_locks),
+ this->provide_base_cb,
+ this->provide_props_cb,
+ this->get_copysrc_kind_cb, this,
+ pool.getPool(), // result pool
+ subPool.getPool()), // scratch pool
+ );
+ m_valid = true;
+}
+
+CommitEditor::~CommitEditor() {}
+
+void CommitEditor::dispose(jobject jthis)
+{
+ if (m_valid)
+ abort();
+
+ static jfieldID fid = 0;
+ SVNBase::dispose(jthis, &fid, JAVAHL_CLASS("/remote/CommitEditor"));
+}
+
+namespace {
+void throw_editor_inactive()
+{
+ JNIUtil::raiseThrowable("java/lang/IllegalStateException",
+ _("The editor is not active"));
+}
+
+void throw_not_implemented(const char* fname)
+{
+ std::string msg = _("Not implemented: ");
+ msg += "CommitEditor.";
+ msg += fname;
+ JNIUtil::raiseThrowable("java/lang/RuntimeException", msg.c_str());
+}
+
+const apr_array_header_t*
+build_children(const Iterator& iter, SVN::Pool& pool)
+{
+ apr_pool_t* result_pool = pool.getPool();
+ apr_array_header_t* children = apr_array_make(
+ result_pool, 0, sizeof(const char*));
+ while (iter.hasNext())
+ {
+ JNIStringHolder path((jstring)iter.next());
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+ APR_ARRAY_PUSH(children, const char*) = path.pstrdup(result_pool);
+ }
+ return children;
+}
+
+svn_checksum_t
+build_checksum(jobject jchecksum, SVN::Pool& pool)
+{
+ apr_pool_t* result_pool = pool.getPool();
+ svn_checksum_t checksum = { 0 };
+ if (jchecksum)
+ {
+ JNIEnv *env = JNIUtil::getEnv();
+
+ static jmethodID digest_mid = 0;
+ static jmethodID kind_mid = 0;
+
+ if (0 == digest_mid || 0 == kind_mid)
+ {
+ jclass cls = env->FindClass(JAVAHL_CLASS("/types/Checksum"));
+ if (JNIUtil::isJavaExceptionThrown())
+ return checksum;
+
+ digest_mid = env->GetMethodID(cls, "getDigest", "()[B");
+ if (JNIUtil::isJavaExceptionThrown())
+ return checksum;
+ kind_mid = env->GetMethodID(cls, "getKind", "()L"
+ JAVAHL_CLASS("/types/Checksum$Kind;"));
+ if (JNIUtil::isJavaExceptionThrown())
+ return checksum;
+ }
+
+ jobject jdigest = env->CallObjectMethod(jchecksum, digest_mid);
+ if (JNIUtil::isJavaExceptionThrown())
+ return checksum;
+ jobject jkind = env->CallObjectMethod(jchecksum, kind_mid);
+ if (JNIUtil::isJavaExceptionThrown())
+ return checksum;
+ JNIByteArray bdigest((jbyteArray)jdigest, true);
+ if (JNIUtil::isJavaExceptionThrown())
+ return checksum;
+
+ void* digest = apr_palloc(result_pool, bdigest.getLength());
+ memcpy(digest, bdigest.getBytes(), bdigest.getLength());
+ checksum.digest = static_cast<const unsigned char*>(digest);
+ checksum.kind = EnumMapper::toChecksumKind(jkind);
+ }
+
+ return checksum;
+}
+} // anonymous namespace
+
+
+void CommitEditor::addDirectory(jstring jrelpath,
+ jobject jchildren, jobject jproperties,
+ jlong jreplaces_revision)
+{
+ if (!m_valid) { throw_editor_inactive(); return; }
+ SVN_JNI_ERR(m_session->m_context->checkCancel(m_session->m_context),);
+
+ Iterator children(jchildren);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ PropertyTable properties(jproperties, true, true);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ SVN::Pool subPool(pool);
+ Relpath relpath(jrelpath, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return;
+ SVN_JNI_ERR(relpath.error_occurred(),);
+
+ SVN_JNI_ERR(svn_editor_add_directory(m_editor, relpath.c_str(),
+ build_children(children, subPool),
+ properties.hash(subPool),
+ svn_revnum_t(jreplaces_revision)),);
+}
+
+void CommitEditor::addFile(jstring jrelpath,
+ jobject jchecksum, jobject jcontents,
+ jobject jproperties,
+ jlong jreplaces_revision)
+{
+ if (!m_valid) { throw_editor_inactive(); return; }
+ SVN_JNI_ERR(m_session->m_context->checkCancel(m_session->m_context),);
+
+ InputStream contents(jcontents);
+ PropertyTable properties(jproperties, true, true);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ SVN::Pool subPool(pool);
+ Relpath relpath(jrelpath, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return;
+ SVN_JNI_ERR(relpath.error_occurred(),);
+
+ svn_checksum_t checksum = build_checksum(jchecksum, subPool);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ SVN_JNI_ERR(svn_editor_add_file(m_editor, relpath.c_str(),
+ &checksum, contents.getStream(subPool),
+ properties.hash(subPool),
+ svn_revnum_t(jreplaces_revision)),);
+}
+
+void CommitEditor::addSymlink(jstring jrelpath,
+ jstring jtarget, jobject jproperties,
+ jlong jreplaces_revision)
+{
+ throw_not_implemented("addSymlink");
+}
+
+void CommitEditor::addAbsent(jstring jrelpath, jobject jkind,
+ jlong jreplaces_revision)
+{
+ if (!m_valid) { throw_editor_inactive(); return; }
+ SVN_JNI_ERR(m_session->m_context->checkCancel(m_session->m_context),);
+
+ SVN::Pool subPool(pool);
+ Relpath relpath(jrelpath, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return;
+ SVN_JNI_ERR(relpath.error_occurred(),);
+
+ SVN_JNI_ERR(svn_editor_add_absent(m_editor, relpath.c_str(),
+ EnumMapper::toNodeKind(jkind),
+ svn_revnum_t(jreplaces_revision)),);
+}
+
+void CommitEditor::alterDirectory(jstring jrelpath, jlong jrevision,
+ jobject jchildren, jobject jproperties)
+{
+ if (!m_valid) { throw_editor_inactive(); return; }
+ SVN_JNI_ERR(m_session->m_context->checkCancel(m_session->m_context),);
+
+ Iterator children(jchildren);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ PropertyTable properties(jproperties, true, false);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ SVN::Pool subPool(pool);
+ Relpath relpath(jrelpath, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return;
+ SVN_JNI_ERR(relpath.error_occurred(),);
+
+ SVN_JNI_ERR(svn_editor_alter_directory(
+ m_editor, relpath.c_str(), svn_revnum_t(jrevision),
+ (jchildren ? build_children(children, subPool) : NULL),
+ properties.hash(subPool)),);
+}
+
+void CommitEditor::alterFile(jstring jrelpath, jlong jrevision,
+ jobject jchecksum, jobject jcontents,
+ jobject jproperties)
+{
+ if (!m_valid) { throw_editor_inactive(); return; }
+ SVN_JNI_ERR(m_session->m_context->checkCancel(m_session->m_context),);
+
+ InputStream contents(jcontents);
+ PropertyTable properties(jproperties, true, false);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ SVN::Pool subPool(pool);
+ Relpath relpath(jrelpath, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return;
+ SVN_JNI_ERR(relpath.error_occurred(),);
+
+ svn_checksum_t checksum = build_checksum(jchecksum, subPool);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ SVN_JNI_ERR(svn_editor_alter_file(
+ m_editor, relpath.c_str(), svn_revnum_t(jrevision),
+ (jcontents ? &checksum : NULL),
+ (jcontents ? contents.getStream(subPool) : NULL),
+ properties.hash(subPool)),);
+}
+
+void CommitEditor::alterSymlink(jstring jrelpath, jlong jrevision,
+ jstring jtarget, jobject jproperties)
+{
+ throw_not_implemented("alterSymlink");
+}
+
+void CommitEditor::remove(jstring jrelpath, jlong jrevision)
+{
+ if (!m_valid) { throw_editor_inactive(); return; }
+ SVN_JNI_ERR(m_session->m_context->checkCancel(m_session->m_context),);
+
+ SVN::Pool subPool(pool);
+ Relpath relpath(jrelpath, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return;
+ SVN_JNI_ERR(relpath.error_occurred(),);
+
+ SVN_JNI_ERR(svn_editor_delete(m_editor, relpath.c_str(),
+ svn_revnum_t(jrevision)),);
+}
+
+void CommitEditor::copy(jstring jsrc_relpath, jlong jsrc_revision,
+ jstring jdst_relpath, jlong jreplaces_revision)
+{
+ if (!m_valid) { throw_editor_inactive(); return; }
+ SVN_JNI_ERR(m_session->m_context->checkCancel(m_session->m_context),);
+
+ SVN::Pool subPool(pool);
+ Relpath src_relpath(jsrc_relpath, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return;
+ SVN_JNI_ERR(src_relpath.error_occurred(),);
+ Relpath dst_relpath(jdst_relpath, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return;
+ SVN_JNI_ERR(dst_relpath.error_occurred(),);
+
+ SVN_JNI_ERR(svn_editor_copy(m_editor,
+ src_relpath.c_str(),
+ svn_revnum_t(jsrc_revision),
+ dst_relpath.c_str(),
+ svn_revnum_t(jreplaces_revision)),);
+}
+
+void CommitEditor::move(jstring jsrc_relpath, jlong jsrc_revision,
+ jstring jdst_relpath, jlong jreplaces_revision)
+{
+ if (!m_valid) { throw_editor_inactive(); return; }
+ SVN_JNI_ERR(m_session->m_context->checkCancel(m_session->m_context),);
+
+ SVN::Pool subPool(pool);
+ Relpath src_relpath(jsrc_relpath, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return;
+ SVN_JNI_ERR(src_relpath.error_occurred(),);
+ Relpath dst_relpath(jdst_relpath, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return;
+ SVN_JNI_ERR(dst_relpath.error_occurred(),);
+
+ SVN_JNI_ERR(svn_editor_move(m_editor,
+ src_relpath.c_str(),
+ svn_revnum_t(jsrc_revision),
+ dst_relpath.c_str(),
+ svn_revnum_t(jreplaces_revision)),);
+}
+
+void CommitEditor::complete()
+{
+ if (!m_valid) { throw_editor_inactive(); return; }
+ SVN_JNI_ERR(m_session->m_context->checkCancel(m_session->m_context),);
+
+ SVN_JNI_ERR(svn_editor_complete(m_editor),);
+ m_valid = false;
+}
+
+void CommitEditor::abort()
+{
+ if (!m_valid) { throw_editor_inactive(); return; }
+ SVN_JNI_ERR(m_session->m_context->checkCancel(m_session->m_context),);
+
+ SVN_JNI_ERR(svn_editor_abort(m_editor),);
+ m_valid = false;
+}
+
+
+namespace {
+svn_error_t* open_callback_session(svn_ra_session_t*& session,
+ const char* url, const char* uuid,
+ RemoteSessionContext* context,
+ SVN::Pool& sessionPool)
+{
+ if (!session)
+ {
+ const char* corrected_url = NULL;
+ SVN_ERR(svn_ra_open4(&session, &corrected_url, url, uuid,
+ context->getCallbacks(),
+ context->getCallbackBaton(),
+ context->getConfigData(),
+ sessionPool.getPool()));
+
+ if (corrected_url)
+ {
+ // This shouldn't happen -- the open session will give us
+ // the final redirected repository URL. There's an edge case
+ // where redirects might change while the session is open;
+ // but we'll just punt handling that to the caller.
+ return svn_error_createf(
+ SVN_ERR_RA_ILLEGAL_URL, NULL,
+ _("Repository URL changed while session was open.\n"
+ "Expected URL: %s\nApparent URL: %s"),
+ url, corrected_url);
+ }
+ }
+ return SVN_NO_ERROR;
+}
+
+void
+invoke_get_base_cb(svn_stream_t **contents, svn_revnum_t *revision,
+ Java::Env env, jobject get_base_cb,
+ const char *repos_relpath, apr_pool_t *result_pool)
+{
+ Java::String relpath(env, repos_relpath);
+ jobject jrv =
+ JavaHL::ProvideBaseCallback(env, get_base_cb)(relpath.get());
+ JavaHL::ProvideBaseCallback::ReturnValue rv(env, jrv);
+ *contents = rv.get_global_stream(result_pool);
+ *revision = svn_revnum_t(rv.get_revision());
+}
+
+void
+invoke_get_props_cb(apr_hash_t **props, svn_revnum_t *revision,
+ Java::Env env, jobject get_props_cb,
+ const char *repos_relpath, apr_pool_t *result_pool)
+{
+ Java::String relpath(env, repos_relpath);
+ jobject jrv =
+ JavaHL::ProvidePropsCallback(env, get_props_cb)(relpath.get());
+ JavaHL::ProvidePropsCallback::ReturnValue rv(env, jrv);
+ *props = rv.get_property_hash(result_pool);
+ *revision = svn_revnum_t(rv.get_revision());
+}
+
+void
+invoke_get_kind_cb(svn_node_kind_t *kind,
+ Java::Env env, jobject get_kind_cb,
+ const char *repos_relpath, svn_revnum_t revision)
+{
+ Java::String relpath(env, repos_relpath);
+ jobject jnode_kind =
+ JavaHL::GetNodeKindCallback(env, get_kind_cb)(relpath.get(),
+ jlong(revision));
+ *kind = EnumMapper::toNodeKind(jnode_kind);
+}
+} // anonymous namespace
+
+
+svn_error_t*
+CommitEditor::provide_base_cb(svn_stream_t **contents,
+ svn_revnum_t *revision,
+ void *baton,
+ const char *repos_relpath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ CommitEditor* editor = static_cast<CommitEditor*>(baton);
+ if (editor->m_get_base_cb.get())
+ {
+ const Java::Env env;
+ SVN_JAVAHL_CATCH(env, SVN_ERR_BASE,
+ invoke_get_base_cb(contents, revision, env,
+ editor->m_get_base_cb.get(),
+ repos_relpath,
+ result_pool));
+ }
+ else
+ {
+ *contents = NULL;
+ *revision = SVN_INVALID_REVNUM;
+ }
+ return SVN_NO_ERROR;
+}
+
+svn_error_t*
+CommitEditor::provide_props_cb(apr_hash_t **props,
+ svn_revnum_t *revision,
+ void *baton,
+ const char *repos_relpath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ CommitEditor* editor = static_cast<CommitEditor*>(baton);
+ if (editor->m_get_props_cb.get())
+ {
+ const Java::Env env;
+ SVN_JAVAHL_CATCH(env, SVN_ERR_BASE,
+ invoke_get_props_cb(props, revision, env,
+ editor->m_get_props_cb.get(),
+ repos_relpath,
+ result_pool));
+ }
+ else
+ {
+ SVN_ERR(open_callback_session(editor->m_callback_session,
+ editor->m_callback_session_url,
+ editor->m_callback_session_uuid,
+ editor->m_session->m_context,
+ editor->pool));
+
+ svn_node_kind_t kind = svn_node_unknown;
+ SVN_ERR(svn_ra_check_path(editor->m_callback_session,
+ repos_relpath, SVN_INVALID_REVNUM, &kind,
+ scratch_pool));
+
+ // FIXME: Getting properties from the youngest revision is in
+ // fact not such a bright idea, as the path may have been moved
+ // or deleted in the repository. On the other hand, if that
+ // happens, the commit would fail due to a conflict anyway.
+ if (kind == svn_node_file)
+ return svn_ra_get_file(editor->m_callback_session,
+ repos_relpath, SVN_INVALID_REVNUM,
+ NULL, revision, props, scratch_pool);
+ else if (kind == svn_node_dir)
+ return svn_ra_get_dir2(editor->m_callback_session, NULL, revision,
+ props, repos_relpath, SVN_INVALID_REVNUM, 0,
+ scratch_pool);
+ else
+ return svn_error_createf(
+ SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
+ _("Expected node kind '%s' or '%s' but got '%s'"),
+ svn_node_kind_to_word(svn_node_file),
+ svn_node_kind_to_word(svn_node_dir),
+ svn_node_kind_to_word(kind));
+ }
+ return SVN_NO_ERROR;
+}
+
+svn_error_t*
+CommitEditor::get_copysrc_kind_cb(svn_node_kind_t* kind, void* baton,
+ const char* repos_relpath,
+ svn_revnum_t src_revision,
+ apr_pool_t *scratch_pool)
+{
+ CommitEditor* editor = static_cast<CommitEditor*>(baton);
+ if (editor->m_get_kind_cb.get())
+ {
+ const Java::Env env;
+ SVN_JAVAHL_CATCH(env, SVN_ERR_BASE,
+ invoke_get_kind_cb(kind, env,
+ editor->m_get_kind_cb.get(),
+ repos_relpath,
+ src_revision));
+ }
+ else
+ {
+ SVN_ERR(open_callback_session(editor->m_callback_session,
+ editor->m_callback_session_url,
+ editor->m_callback_session_uuid,
+ editor->m_session->m_context,
+ editor->pool));
+
+ return svn_ra_check_path(editor->m_callback_session,
+ repos_relpath, src_revision, kind,
+ scratch_pool);
+ }
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/bindings/javahl/native/CommitEditor.h b/subversion/bindings/javahl/native/CommitEditor.h
new file mode 100644
index 0000000..4160b6c
--- /dev/null
+++ b/subversion/bindings/javahl/native/CommitEditor.h
@@ -0,0 +1,132 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file CommitEditor.h
+ * @brief Interface of the class CommitEditor
+ */
+
+#ifndef JAVAHL_COMMIT_EDITOR_H
+#define JAVAHL_COMMIT_EDITOR_H
+
+#include <string>
+#include <jni.h>
+
+#include "svn_ra.h"
+
+#include "JNIUtil.h"
+#include "SVNBase.h"
+#include "CommitCallback.h"
+
+#include "jniwrapper/jni_globalref.hpp"
+
+class RemoteSession;
+
+// Forward-declare the currently private EV2 editor struct.
+struct svn_editor_t;
+
+/*
+ * This class wraps an EV2 commit editor.
+ */
+class CommitEditor : public SVNBase
+{
+public:
+ static CommitEditor* getCppObject(jobject jthis);
+ static jlong createInstance(jobject jsession,
+ jobject jrevprops,
+ jobject jcommit_callback,
+ jobject jlock_tokens,
+ jboolean jkeep_locks,
+ jobject jget_base_cb,
+ jobject jget_props_cb,
+ jobject jget_kind_cb);
+ virtual ~CommitEditor();
+
+ virtual void dispose(jobject jthis);
+
+ void addDirectory(jstring jrelpath,
+ jobject jchildren, jobject jproperties,
+ jlong jreplaces_revision);
+ void addFile(jstring jrelpath,
+ jobject jchecksum, jobject jcontents,
+ jobject jproperties,
+ jlong jreplaces_revision);
+ void addSymlink(jstring jrelpath,
+ jstring jtarget, jobject jproperties,
+ jlong jreplaces_revision);
+ void addAbsent(jstring jrelpath,
+ jobject jkind, jlong jreplaces_revision);
+ void alterDirectory(jstring jrelpath, jlong jrevision,
+ jobject jchildren, jobject jproperties);
+ void alterFile(jstring jrelpath, jlong jrevision,
+ jobject jchecksum, jobject jcontents,
+ jobject jproperties);
+ void alterSymlink(jstring jrelpath, jlong jrevision,
+ jstring jtarget, jobject jproperties);
+ void remove(jstring jrelpath, jlong jrevision);
+ void copy(jstring jsrc_relpath, jlong jsrc_revision,
+ jstring jdst_relpath, jlong jreplaces_revision);
+ void move(jstring jsrc_relpath, jlong jsrc_revision,
+ jstring jdst_relpath, jlong jreplaces_revision);
+ void complete();
+ void abort();
+
+private:
+ CommitEditor(RemoteSession* session,
+ jobject jrevprops, jobject jcommit_callback,
+ jobject jlock_tokens, jboolean jkeep_locks,
+ jobject jget_base_cb, jobject jget_props_cb,
+ jobject jget_kind_cb);
+
+ // This is our private callbacks for the commit editor.
+ static svn_error_t* provide_base_cb(svn_stream_t **contents,
+ svn_revnum_t *revision,
+ void *baton,
+ const char *repos_relpath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+ static svn_error_t* provide_props_cb(apr_hash_t **props,
+ svn_revnum_t *revision,
+ void *baton,
+ const char *repos_relpath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+ static svn_error_t* get_copysrc_kind_cb(svn_node_kind_t* kind, void* baton,
+ const char* repos_relpath,
+ svn_revnum_t src_revision,
+ apr_pool_t *scratch_pool);
+
+ bool m_valid;
+ PersistentCommitCallback m_callback;
+ RemoteSession* m_session;
+ svn_editor_t* m_editor;
+
+ Java::GlobalObject m_get_base_cb;
+ Java::GlobalObject m_get_props_cb;
+ Java::GlobalObject m_get_kind_cb;
+
+ // Temporary, while EV2 shims are in place
+ svn_ra_session_t* m_callback_session;
+ const char* m_callback_session_url;
+ const char* m_callback_session_uuid;
+};
+
+#endif // JAVAHL_COMMIT_EDITOR_H
diff --git a/subversion/bindings/javahl/native/CommitMessage.cpp b/subversion/bindings/javahl/native/CommitMessage.cpp
index a947f97..51bd83d 100644
--- a/subversion/bindings/javahl/native/CommitMessage.cpp
+++ b/subversion/bindings/javahl/native/CommitMessage.cpp
@@ -65,6 +65,7 @@ CommitMessage::getCommitMessage(const char **log_msg,
const apr_array_header_t *commit_items,
apr_pool_t *pool)
{
+ *log_msg = NULL;
*tmp_file = NULL;
JNIEnv *env = JNIUtil::getEnv();
@@ -72,7 +73,7 @@ CommitMessage::getCommitMessage(const char **log_msg,
static jmethodID midCallback = 0;
if (midCallback == 0)
{
- jclass clazz2 = env->FindClass(JAVA_PACKAGE"/callback/CommitMessageCallback");
+ jclass clazz2 = env->FindClass(JAVAHL_CLASS("/callback/CommitMessageCallback"));
if (JNIUtil::isJavaExceptionThrown())
return SVN_NO_ERROR;
@@ -93,6 +94,9 @@ CommitMessage::getCommitMessage(const char **log_msg,
jobject jitem = CreateJ::CommitItem(item);
+ if (!jitem)
+ return SVN_NO_ERROR; /* Exception thrown */
+
// store the Java object into the array
jitems.push_back(jitem);
}
@@ -102,7 +106,7 @@ CommitMessage::getCommitMessage(const char **log_msg,
midCallback,
CreateJ::Set(jitems));
if (JNIUtil::isJavaExceptionThrown())
- return SVN_NO_ERROR;
+ POP_AND_RETURN_EXCEPTION_AS_SVNERROR();
if (jmessage != NULL)
{
diff --git a/subversion/bindings/javahl/native/CopySources.cpp b/subversion/bindings/javahl/native/CopySources.cpp
index 0ae471d..a1d93f4 100644
--- a/subversion/bindings/javahl/native/CopySources.cpp
+++ b/subversion/bindings/javahl/native/CopySources.cpp
@@ -60,7 +60,7 @@ CopySources::makeJCopySource(const char *path, svn_revnum_t rev, SVN::Pool &pool
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
- jclass clazz = env->FindClass(JAVA_PACKAGE "/types/CopySource");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/types/CopySource"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
@@ -69,8 +69,8 @@ CopySources::makeJCopySource(const char *path, svn_revnum_t rev, SVN::Pool &pool
{
ctor = env->GetMethodID(clazz, "<init>",
"(Ljava/lang/String;"
- "L" JAVA_PACKAGE "/types/Revision;"
- "L" JAVA_PACKAGE "/types/Revision;)V");
+ JAVAHL_ARG("/types/Revision;")
+ JAVAHL_ARG("/types/Revision;") ")V");
if (JNIUtil::isExceptionThrown())
POP_AND_RETURN_NULL;
}
@@ -88,7 +88,7 @@ CopySources::array(SVN::Pool &pool)
apr_pool_t *p = pool.getPool();
JNIEnv *env = JNIUtil::getEnv();
- jclass clazz = env->FindClass(JAVA_PACKAGE "/types/CopySource");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/types/CopySource"));
if (JNIUtil::isJavaExceptionThrown())
return NULL;
@@ -132,7 +132,7 @@ CopySources::array(SVN::Pool &pool)
if (getRevision == 0)
{
getRevision = env->GetMethodID(clazz, "getRevision",
- "()L"JAVA_PACKAGE"/types/Revision;");
+ "()" JAVAHL_ARG("/types/Revision;"));
if (JNIUtil::isJavaExceptionThrown() || getRevision == 0)
return NULL;
}
@@ -151,7 +151,7 @@ CopySources::array(SVN::Pool &pool)
if (getPegRevision == 0)
{
getPegRevision = env->GetMethodID(clazz, "getPegRevision",
- "()L"JAVA_PACKAGE"/types/Revision;");
+ "()" JAVAHL_ARG("/types/Revision;"));
if (JNIUtil::isJavaExceptionThrown() || getPegRevision == 0)
return NULL;
}
diff --git a/subversion/bindings/javahl/native/CreateJ.cpp b/subversion/bindings/javahl/native/CreateJ.cpp
index 9b40ce7..d9dffa2 100644
--- a/subversion/bindings/javahl/native/CreateJ.cpp
+++ b/subversion/bindings/javahl/native/CreateJ.cpp
@@ -30,12 +30,14 @@
#include "JNIStringHolder.h"
#include "EnumMapper.h"
#include "RevisionRange.h"
+#include "RevisionRangeList.h"
#include "CreateJ.h"
#include "../include/org_apache_subversion_javahl_types_Revision.h"
#include "../include/org_apache_subversion_javahl_CommitItemStateFlags.h"
#include "svn_path.h"
#include "svn_props.h"
+#include "svn_mergeinfo.h"
#include "private/svn_wc_private.h"
jobject
@@ -52,7 +54,7 @@ CreateJ::ConflictDescriptor(const svn_wc_conflict_description2_t *desc)
return NULL;
// Create an instance of the conflict descriptor.
- jclass clazz = env->FindClass(JAVA_PACKAGE "/ConflictDescriptor");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/ConflictDescriptor"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
@@ -60,16 +62,17 @@ CreateJ::ConflictDescriptor(const svn_wc_conflict_description2_t *desc)
if (ctor == 0)
{
ctor = env->GetMethodID(clazz, "<init>", "(Ljava/lang/String;"
- "L"JAVA_PACKAGE"/ConflictDescriptor$Kind;"
- "L"JAVA_PACKAGE"/types/NodeKind;"
+ JAVAHL_ARG("/ConflictDescriptor$Kind;")
+ JAVAHL_ARG("/types/NodeKind;")
"Ljava/lang/String;ZLjava/lang/String;"
- "L"JAVA_PACKAGE"/ConflictDescriptor$Action;"
- "L"JAVA_PACKAGE"/ConflictDescriptor$Reason;"
- "L"JAVA_PACKAGE"/ConflictDescriptor$Operation;"
+ JAVAHL_ARG("/ConflictDescriptor$Action;")
+ JAVAHL_ARG("/ConflictDescriptor$Reason;")
+ JAVAHL_ARG("/ConflictDescriptor$Operation;")
"Ljava/lang/String;Ljava/lang/String;"
"Ljava/lang/String;Ljava/lang/String;"
- "L"JAVA_PACKAGE"/types/ConflictVersion;"
- "L"JAVA_PACKAGE"/types/ConflictVersion;)V");
+ JAVAHL_ARG("/types/ConflictVersion;")
+ JAVAHL_ARG("/types/ConflictVersion;")
+ "Ljava/lang/String;[B[B[B[B)V");
if (JNIUtil::isJavaExceptionThrown() || ctor == 0)
POP_AND_RETURN_NULL;
}
@@ -116,6 +119,33 @@ CreateJ::ConflictDescriptor(const svn_wc_conflict_description2_t *desc)
jobject joperation = EnumMapper::mapOperation(desc->operation);
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
+ jstring jpropRejectAbspath = JNIUtil::makeJString(desc->prop_reject_abspath);
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+ jbyteArray jpropValueBase = (
+ !desc->prop_value_base ? NULL
+ :JNIUtil::makeJByteArray(desc->prop_value_base->data,
+ int(desc->prop_value_base->len)));
+ if (JNIUtil::isExceptionThrown())
+ POP_AND_RETURN_NULL;
+ jbyteArray jpropValueWorking = (
+ !desc->prop_value_working ? NULL
+ :JNIUtil::makeJByteArray(desc->prop_value_working->data,
+ int(desc->prop_value_working->len)));
+ if (JNIUtil::isExceptionThrown())
+ POP_AND_RETURN_NULL;
+ jbyteArray jpropValueIncomingOld = (
+ !desc->prop_value_incoming_old ? NULL
+ :JNIUtil::makeJByteArray(desc->prop_value_incoming_old->data,
+ int(desc->prop_value_incoming_old->len)));
+ if (JNIUtil::isExceptionThrown())
+ POP_AND_RETURN_NULL;
+ jbyteArray jpropValueIncomingNew = (
+ !desc->prop_value_incoming_new ? NULL
+ :JNIUtil::makeJByteArray(desc->prop_value_incoming_new->data,
+ int(desc->prop_value_incoming_new->len)));
+ if (JNIUtil::isExceptionThrown())
+ POP_AND_RETURN_NULL;
// Instantiate the conflict descriptor.
jobject jdesc = env->NewObject(clazz, ctor, jpath, jconflictKind,
@@ -123,7 +153,10 @@ CreateJ::ConflictDescriptor(const svn_wc_conflict_description2_t *desc)
(jboolean) desc->is_binary, jmimeType,
jconflictAction, jconflictReason, joperation,
jbasePath, jreposPath, juserPath,
- jmergedPath, jsrcLeft, jsrcRight);
+ jmergedPath, jsrcLeft, jsrcRight,
+ jpropRejectAbspath, jpropValueBase,
+ jpropValueWorking, jpropValueIncomingOld,
+ jpropValueIncomingNew);
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
@@ -144,7 +177,7 @@ CreateJ::ConflictVersion(const svn_wc_conflict_version_t *version)
return NULL;
// Create an instance of the conflict version.
- jclass clazz = env->FindClass(JAVA_PACKAGE "/types/ConflictVersion");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/types/ConflictVersion"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
@@ -154,7 +187,7 @@ CreateJ::ConflictVersion(const svn_wc_conflict_version_t *version)
ctor = env->GetMethodID(clazz, "<init>", "(Ljava/lang/String;"
"Ljava/lang/String;J"
"Ljava/lang/String;"
- "L"JAVA_PACKAGE"/types/NodeKind;"
+ JAVAHL_ARG("/types/NodeKind;")
")V");
if (JNIUtil::isJavaExceptionThrown() || ctor == 0)
POP_AND_RETURN_NULL;
@@ -195,7 +228,7 @@ CreateJ::Checksum(const svn_checksum_t *checksum)
if (JNIUtil::isJavaExceptionThrown())
return NULL;
- jclass clazz = env->FindClass(JAVA_PACKAGE"/types/Checksum");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/types/Checksum"));
if (JNIUtil::isExceptionThrown())
POP_AND_RETURN_NULL;
@@ -205,7 +238,7 @@ CreateJ::Checksum(const svn_checksum_t *checksum)
{
midConstructor = env->GetMethodID(clazz, "<init>",
"([B"
- "L"JAVA_PACKAGE"/types/Checksum$Kind;"
+ JAVAHL_ARG("/types/Checksum$Kind;")
")V");
if (JNIUtil::isExceptionThrown())
POP_AND_RETURN_NULL;
@@ -230,6 +263,61 @@ CreateJ::Checksum(const svn_checksum_t *checksum)
}
jobject
+CreateJ::DirEntry(const char *path, const char *absPath,
+ const svn_dirent_t *dirent)
+{
+ JNIEnv *env = JNIUtil::getEnv();
+
+ // Create a local frame for our references
+ env->PushLocalFrame(LOCAL_FRAME_SIZE);
+ if (JNIUtil::isJavaExceptionThrown())
+ return SVN_NO_ERROR;
+
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/types/DirEntry"));
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+
+ static jmethodID mid = 0;
+ if (mid == 0)
+ {
+ mid = env->GetMethodID(clazz, "<init>",
+ "(Ljava/lang/String;Ljava/lang/String;"
+ JAVAHL_ARG("/types/NodeKind;")
+ "JZJJLjava/lang/String;)V");
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+ }
+
+ jstring jPath = JNIUtil::makeJString(path);
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+
+ jstring jAbsPath = JNIUtil::makeJString(absPath);
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+
+ jobject jNodeKind = EnumMapper::mapNodeKind(dirent->kind);
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+
+ jlong jSize = dirent->size;
+ jboolean jHasProps = (dirent->has_props? JNI_TRUE : JNI_FALSE);
+ jlong jLastChangedRevision = dirent->created_rev;
+ jlong jLastChanged = dirent->time;
+ jstring jLastAuthor = JNIUtil::makeJString(dirent->last_author);
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+
+ jobject ret = env->NewObject(clazz, mid, jPath, jAbsPath, jNodeKind,
+ jSize, jHasProps, jLastChangedRevision,
+ jLastChanged, jLastAuthor);
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+
+ return env->PopLocalFrame(ret);
+}
+
+jobject
CreateJ::Info(const char *path, const svn_client_info2_t *info)
{
JNIEnv *env = JNIUtil::getEnv();
@@ -239,7 +327,7 @@ CreateJ::Info(const char *path, const svn_client_info2_t *info)
if (JNIUtil::isJavaExceptionThrown())
return NULL;
- jclass clazz = env->FindClass(JAVA_PACKAGE "/types/Info");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/types/Info"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
@@ -249,15 +337,16 @@ CreateJ::Info(const char *path, const svn_client_info2_t *info)
mid = env->GetMethodID(clazz, "<init>",
"(Ljava/lang/String;Ljava/lang/String;"
"Ljava/lang/String;J"
- "L"JAVA_PACKAGE"/types/NodeKind;"
+ JAVAHL_ARG("/types/NodeKind;")
"Ljava/lang/String;Ljava/lang/String;"
"JJLjava/lang/String;"
- "L"JAVA_PACKAGE"/types/Lock;Z"
- "L"JAVA_PACKAGE"/types/Info$ScheduleKind;"
+ JAVAHL_ARG("/types/Lock;Z")
+ JAVAHL_ARG("/types/Info$ScheduleKind;")
"Ljava/lang/String;JJ"
- "L"JAVA_PACKAGE"/types/Checksum;"
+ JAVAHL_ARG("/types/Checksum;")
"Ljava/lang/String;JJ"
- "L"JAVA_PACKAGE"/types/Depth;Ljava/util/Set;)V");
+ JAVAHL_ARG("/types/Depth;Ljava/util/Set;")
+ ")V");
if (mid == 0 || JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
}
@@ -384,7 +473,7 @@ CreateJ::Lock(const svn_lock_t *lock)
if (JNIUtil::isJavaExceptionThrown())
return NULL;
- jclass clazz = env->FindClass(JAVA_PACKAGE"/types/Lock");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/types/Lock"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
@@ -423,6 +512,72 @@ CreateJ::Lock(const svn_lock_t *lock)
}
jobject
+CreateJ::LockMap(const apr_hash_t *locks, apr_pool_t *pool)
+{
+ JNIEnv *env = JNIUtil::getEnv();
+
+ if (locks == NULL)
+ return NULL;
+
+ // Create a local frame for our references
+ env->PushLocalFrame(LOCAL_FRAME_SIZE);
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+
+ jclass clazz = env->FindClass("java/util/HashMap");
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+
+ static jmethodID init_mid = 0;
+ if (init_mid == 0)
+ {
+ init_mid = env->GetMethodID(clazz, "<init>", "()V");
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+ }
+
+ static jmethodID put_mid = 0;
+ if (put_mid == 0)
+ {
+ put_mid = env->GetMethodID(clazz, "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)"
+ "Ljava/lang/Object;");
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+ }
+
+ jobject map = env->NewObject(clazz, init_mid);
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+
+ apr_hash_index_t *hi;
+ int i = 0;
+ for (hi = apr_hash_first(pool, (apr_hash_t *) locks); hi;
+ hi = apr_hash_next(hi), ++i)
+ {
+ const char *key = (const char *) apr_hash_this_key(hi);
+ const svn_lock_t *lock = (const svn_lock_t *) apr_hash_this_val(hi);
+
+ jstring jpath = JNIUtil::makeJString(key);
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+
+ jobject jlock = Lock(lock);
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+
+ env->CallObjectMethod(map, put_mid, jpath, jlock);
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+
+ env->DeleteLocalRef(jpath);
+ env->DeleteLocalRef(jlock);
+ }
+
+ return env->PopLocalFrame(map);
+}
+
+jobject
CreateJ::ChangedPath(const char *path, svn_log_changed_path2_t *log_item)
{
JNIEnv *env = JNIUtil::getEnv();
@@ -432,7 +587,7 @@ CreateJ::ChangedPath(const char *path, svn_log_changed_path2_t *log_item)
if (JNIUtil::isJavaExceptionThrown())
return NULL;
- jclass clazzCP = env->FindClass(JAVA_PACKAGE"/types/ChangePath");
+ jclass clazzCP = env->FindClass(JAVAHL_CLASS("/types/ChangePath"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
@@ -442,10 +597,11 @@ CreateJ::ChangedPath(const char *path, svn_log_changed_path2_t *log_item)
midCP = env->GetMethodID(clazzCP,
"<init>",
"(Ljava/lang/String;JLjava/lang/String;"
- "L"JAVA_PACKAGE"/types/ChangePath$Action;"
- "L"JAVA_PACKAGE"/types/NodeKind;"
- "L"JAVA_PACKAGE"/types/Tristate;"
- "L"JAVA_PACKAGE"/types/Tristate;)V");
+ JAVAHL_ARG("/types/ChangePath$Action;")
+ JAVAHL_ARG("/types/NodeKind;")
+ JAVAHL_ARG("/types/Tristate;")
+ JAVAHL_ARG("/types/Tristate;")
+ ")V");
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
}
@@ -493,7 +649,7 @@ CreateJ::Status(svn_wc_context_t *wc_ctx,
if (JNIUtil::isJavaExceptionThrown())
return NULL;
- jclass clazz = env->FindClass(JAVA_PACKAGE"/types/Status");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/types/Status"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
@@ -502,15 +658,18 @@ CreateJ::Status(svn_wc_context_t *wc_ctx,
{
mid = env->GetMethodID(clazz, "<init>",
"(Ljava/lang/String;Ljava/lang/String;"
- "L"JAVA_PACKAGE"/types/NodeKind;"
+ JAVAHL_ARG("/types/NodeKind;")
"JJJLjava/lang/String;"
- "L"JAVA_PACKAGE"/types/Status$Kind;"
- "L"JAVA_PACKAGE"/types/Status$Kind;"
- "L"JAVA_PACKAGE"/types/Status$Kind;"
- "L"JAVA_PACKAGE"/types/Status$Kind;"
- "ZZZZZL"JAVA_PACKAGE"/types/Lock;"
- "L"JAVA_PACKAGE"/types/Lock;"
- "JJL"JAVA_PACKAGE"/types/NodeKind;"
+ JAVAHL_ARG("/types/Status$Kind;")
+ JAVAHL_ARG("/types/Status$Kind;")
+ JAVAHL_ARG("/types/Status$Kind;")
+ JAVAHL_ARG("/types/Status$Kind;")
+ JAVAHL_ARG("/types/Status$Kind;")
+ JAVAHL_ARG("/types/Status$Kind;")
+ "ZZ" JAVAHL_ARG("/types/Depth;")
+ "ZZZ" JAVAHL_ARG("/types/Lock;")
+ JAVAHL_ARG("/types/Lock;")
+ "JJ" JAVAHL_ARG("/types/NodeKind;")
"Ljava/lang/String;Ljava/lang/String;"
"Ljava/lang/String;Ljava/lang/String;)V");
if (JNIUtil::isJavaExceptionThrown())
@@ -534,27 +693,16 @@ CreateJ::Status(svn_wc_context_t *wc_ctx,
jstring jMovedFromAbspath = NULL;
jstring jMovedToAbspath = NULL;
- enum svn_wc_status_kind text_status = status->node_status;
-
- /* Avoid using values that might come from prop changes */
- if (text_status == svn_wc_status_modified
- || text_status == svn_wc_status_conflicted)
- text_status = status->text_status;
-
- enum svn_wc_status_kind repos_text_status = status->repos_node_status;
-
- if (repos_text_status == svn_wc_status_modified
- || repos_text_status == svn_wc_status_conflicted)
- repos_text_status = status->repos_text_status;
-
- jboolean jIsConflicted = (status->conflicted == 1) ? JNI_TRUE : JNI_FALSE;
- jobject jTextType = EnumMapper::mapStatusKind(text_status);
+ jobject jNodeType = EnumMapper::mapStatusKind(status->node_status);
+ jobject jTextType = EnumMapper::mapStatusKind(status->text_status);
jobject jPropType = EnumMapper::mapStatusKind(status->prop_status);
- jobject jRepositoryTextType = EnumMapper::mapStatusKind(repos_text_status);
- jobject jRepositoryPropType = EnumMapper::mapStatusKind(
- status->repos_prop_status);
- jboolean jIsCopied = (status->copied == 1) ? JNI_TRUE: JNI_FALSE;
+ jobject jRpNodeType = EnumMapper::mapStatusKind(status->repos_node_status);
+ jobject jRpTextType = EnumMapper::mapStatusKind(status->repos_text_status);
+ jobject jRpPropType = EnumMapper::mapStatusKind(status->repos_prop_status);
+ jobject jDepth = EnumMapper::mapDepth(status->depth);
jboolean jIsLocked = (status->wc_is_locked == 1) ? JNI_TRUE: JNI_FALSE;
+ jboolean jIsCopied = (status->copied == 1) ? JNI_TRUE: JNI_FALSE;
+ jboolean jIsConflicted = (status->conflicted == 1) ? JNI_TRUE : JNI_FALSE;
jboolean jIsSwitched = (status->switched == 1) ? JNI_TRUE: JNI_FALSE;
jboolean jIsFileExternal = (status->file_external == 1) ? JNI_TRUE
: JNI_FALSE;
@@ -620,9 +768,10 @@ CreateJ::Status(svn_wc_context_t *wc_ctx,
jobject ret = env->NewObject(clazz, mid, jPath, jUrl, jNodeKind, jRevision,
jLastChangedRevision, jLastChangedDate,
- jLastCommitAuthor, jTextType, jPropType,
- jRepositoryTextType, jRepositoryPropType,
- jIsLocked, jIsCopied, jIsConflicted,
+ jLastCommitAuthor,
+ jNodeType, jTextType, jPropType,
+ jRpNodeType, jRpTextType, jRpPropType,
+ jIsLocked, jIsCopied, jDepth, jIsConflicted,
jIsSwitched, jIsFileExternal, jLocalLock,
jReposLock,
jOODLastCmtRevision, jOODLastCmtDate,
@@ -643,7 +792,7 @@ CreateJ::ClientNotifyInformation(const svn_wc_notify_t *wcNotify)
return NULL;
static jmethodID midCT = 0;
- jclass clazz = env->FindClass(JAVA_PACKAGE"/ClientNotifyInformation");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/ClientNotifyInformation"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
@@ -651,16 +800,17 @@ CreateJ::ClientNotifyInformation(const svn_wc_notify_t *wcNotify)
{
midCT = env->GetMethodID(clazz, "<init>",
"(Ljava/lang/String;"
- "L"JAVA_PACKAGE"/ClientNotifyInformation$Action;"
- "L"JAVA_PACKAGE"/types/NodeKind;"
- "Ljava/lang/String;"
- "L"JAVA_PACKAGE"/types/Lock;"
+ JAVAHL_ARG("/ClientNotifyInformation$Action;")
+ JAVAHL_ARG("/types/NodeKind;")
"Ljava/lang/String;"
- "L"JAVA_PACKAGE"/ClientNotifyInformation$Status;"
- "L"JAVA_PACKAGE"/ClientNotifyInformation$Status;"
- "L"JAVA_PACKAGE"/ClientNotifyInformation$LockStatus;"
+ JAVAHL_ARG("/types/Lock;")
+ "Ljava/lang/String;Ljava/util/List;"
+ JAVAHL_ARG("/ClientNotifyInformation$Status;")
+ JAVAHL_ARG("/ClientNotifyInformation$Status;")
+ JAVAHL_ARG("/ClientNotifyInformation$LockStatus;")
"JLjava/lang/String;"
- "L"JAVA_PACKAGE"/types/RevisionRange;"
+ JAVAHL_ARG("/types/RevisionRange;")
+ "Ljava/lang/String;"
"Ljava/lang/String;Ljava/lang/String;"
"Ljava/util/Map;JJJJJJI)V");
if (JNIUtil::isJavaExceptionThrown() || midCT == 0)
@@ -688,7 +838,9 @@ CreateJ::ClientNotifyInformation(const svn_wc_notify_t *wcNotify)
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
- jstring jErr = JNIUtil::makeSVNErrorMessage(wcNotify->err);
+ jstring jErr;
+ jobject jErrStack;
+ JNIUtil::makeSVNErrorMessage(wcNotify->err, &jErr, &jErrStack);
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
@@ -716,6 +868,10 @@ CreateJ::ClientNotifyInformation(const svn_wc_notify_t *wcNotify)
POP_AND_RETURN_NULL;
}
+ jstring jUrl = JNIUtil::makeJString(wcNotify->url);
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+
jstring jpathPrefix = JNIUtil::makeJString(wcNotify->path_prefix);
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
@@ -735,7 +891,7 @@ CreateJ::ClientNotifyInformation(const svn_wc_notify_t *wcNotify)
jlong jhunkModifiedLength = wcNotify->hunk_modified_length;
jlong jhunkMatchedLine = wcNotify->hunk_matched_line;
jint jhunkFuzz = static_cast<jint>(wcNotify->hunk_fuzz);
- if (jhunkFuzz != wcNotify->hunk_fuzz)
+ if (jhunkFuzz < 0 || jhunkFuzz != wcNotify->hunk_fuzz)
{
env->ThrowNew(env->FindClass("java.lang.ArithmeticException"),
"Overflow converting C svn_linenum_t to Java int");
@@ -744,10 +900,10 @@ CreateJ::ClientNotifyInformation(const svn_wc_notify_t *wcNotify)
// call the Java method
jobject jInfo = env->NewObject(clazz, midCT, jPath, jAction,
- jKind, jMimeType, jLock, jErr,
+ jKind, jMimeType, jLock, jErr, jErrStack,
jContentState, jPropState, jLockState,
(jlong) wcNotify->revision, jChangelistName,
- jMergeRange, jpathPrefix, jpropName,
+ jMergeRange, jUrl, jpathPrefix, jpropName,
jrevProps, joldRevision,
jhunkOriginalStart, jhunkOriginalLength,
jhunkModifiedStart, jhunkModifiedLength,
@@ -769,16 +925,16 @@ CreateJ::ReposNotifyInformation(const svn_repos_notify_t *reposNotify)
return NULL;
static jmethodID midCT = 0;
- jclass clazz = env->FindClass(JAVA_PACKAGE"/ReposNotifyInformation");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/ReposNotifyInformation"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
if (midCT == 0)
{
midCT = env->GetMethodID(clazz, "<init>",
- "(L"JAVA_PACKAGE"/ReposNotifyInformation$Action;"
+ "(" JAVAHL_ARG("/ReposNotifyInformation$Action;")
"JLjava/lang/String;JJJ"
- "L"JAVA_PACKAGE"/ReposNotifyInformation$NodeAction;"
+ JAVAHL_ARG("/ReposNotifyInformation$NodeAction;")
"Ljava/lang/String;)V");
if (JNIUtil::isJavaExceptionThrown() || midCT == 0)
POP_AND_RETURN_NULL;
@@ -827,7 +983,7 @@ CreateJ::CommitItem(svn_client_commit_item3_t *item)
if (JNIUtil::isJavaExceptionThrown())
return NULL;
- jclass clazz = env->FindClass(JAVA_PACKAGE"/CommitItem");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/CommitItem"));
if (JNIUtil::isExceptionThrown())
POP_AND_RETURN_NULL;
@@ -837,7 +993,7 @@ CreateJ::CommitItem(svn_client_commit_item3_t *item)
{
midConstructor = env->GetMethodID(clazz, "<init>",
"(Ljava/lang/String;"
- "L"JAVA_PACKAGE"/types/NodeKind;"
+ JAVAHL_ARG("/types/NodeKind;")
"ILjava/lang/String;"
"Ljava/lang/String;J"
"Ljava/lang/String;)V");
@@ -909,7 +1065,7 @@ CreateJ::CommitInfo(const svn_commit_info_t *commit_info)
return NULL;
static jmethodID midCT = 0;
- jclass clazz = env->FindClass(JAVA_PACKAGE"/CommitInfo");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/CommitInfo"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
@@ -951,82 +1107,129 @@ CreateJ::CommitInfo(const svn_commit_info_t *commit_info)
}
jobject
-CreateJ::RevisionRangeList(svn_rangelist_t *ranges)
+CreateJ::StringSet(const apr_array_header_t *strings)
{
+ std::vector<jobject> jstrs;
+
+ for (int i = 0; i < strings->nelts; ++i)
+ {
+ const char *str = APR_ARRAY_IDX(strings, i, const char *);
+ jstring jstr = JNIUtil::makeJString(str);
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+
+ jstrs.push_back(jstr);
+ }
+
+ return CreateJ::Set(jstrs);
+}
+
+namespace {
+void fill_property_map(jobject map,
+ apr_hash_t* prop_hash, apr_array_header_t* prop_diffs,
+ apr_pool_t* scratch_pool, jmethodID put_mid)
+{
+ SVN_ERR_ASSERT_NO_RETURN(!(prop_hash && prop_diffs));
+
+ if (!map || (prop_hash == NULL && prop_diffs == NULL))
+ return;
+
JNIEnv *env = JNIUtil::getEnv();
// Create a local frame for our references
env->PushLocalFrame(LOCAL_FRAME_SIZE);
if (JNIUtil::isJavaExceptionThrown())
- return NULL;
-
- jclass clazz = env->FindClass("java/util/ArrayList");
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
+ return;
- static jmethodID init_mid = 0;
- if (init_mid == 0)
+ // The caller may not know the concrete class of the map, so
+ // determine the "put" method identifier here.
+ if (put_mid == 0)
{
- init_mid = env->GetMethodID(clazz, "<init>", "()V");
+ put_mid = env->GetMethodID(env->GetObjectClass(map), "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)"
+ "Ljava/lang/Object;");
if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
+ POP_AND_RETURN_NOTHING();
}
- static jmethodID add_mid = 0;
- if (add_mid == 0)
+ struct body
+ {
+ void operator()(const char* key, const svn_string_t* val)
+ {
+ jstring jpropName = JNIUtil::makeJString(key);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ jbyteArray jpropVal = (val ? JNIUtil::makeJByteArray(val) : NULL);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ jobject ret = m_env->CallObjectMethod(m_map, m_put_mid,
+ jpropName, jpropVal);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ m_env->DeleteLocalRef(ret);
+ m_env->DeleteLocalRef(jpropVal);
+ m_env->DeleteLocalRef(jpropName);
+ }
+
+ JNIEnv*& m_env;
+ jmethodID& m_put_mid;
+ jobject& m_map;
+
+ body(JNIEnv*& xenv, jmethodID& xput_mid, jobject& xmap)
+ : m_env(xenv), m_put_mid(xput_mid), m_map(xmap)
+ {}
+ } loop_body(env, put_mid, map);
+
+ if (prop_hash)
{
- add_mid = env->GetMethodID(clazz, "add", "(Ljava/lang/Object;)Z");
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
- }
+ if (!scratch_pool)
+ scratch_pool = apr_hash_pool_get(prop_hash);
- jobject jranges = env->NewObject(clazz, init_mid);
+ apr_hash_index_t *hi;
+ for (hi = apr_hash_first(scratch_pool, prop_hash);
+ hi; hi = apr_hash_next(hi))
+ {
+ const char* key;
+ svn_string_t* val;
- for (int i = 0; i < ranges->nelts; ++i)
- {
- // Convert svn_merge_range_t *'s to Java RevisionRange objects.
- svn_merge_range_t *range =
- APR_ARRAY_IDX(ranges, i, svn_merge_range_t *);
+ const void* v_key;
+ void* v_val;
- jobject jrange = RevisionRange::makeJRevisionRange(range);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
+ apr_hash_this(hi, &v_key, NULL, &v_val);
+ key = static_cast<const char*>(v_key);
+ val = static_cast<svn_string_t*>(v_val);
- env->CallBooleanMethod(jranges, add_mid, jrange);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
-
- env->DeleteLocalRef(jrange);
+ loop_body(key, val);
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NOTHING();
+ }
}
-
- return env->PopLocalFrame(jranges);
-}
-
-jobject
-CreateJ::StringSet(apr_array_header_t *strings)
-{
- std::vector<jobject> jstrs;
-
- for (int i = 0; i < strings->nelts; ++i)
+ else
{
- const char *str = APR_ARRAY_IDX(strings, i, const char *);
- jstring jstr = JNIUtil::makeJString(str);
- if (JNIUtil::isJavaExceptionThrown())
- return NULL;
-
- jstrs.push_back(jstr);
+ for (int i = 0; i < prop_diffs->nelts; ++i)
+ {
+ svn_prop_t* prop = &APR_ARRAY_IDX(prop_diffs, i, svn_prop_t);
+ loop_body(prop->name, prop->value);
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NOTHING();
+ }
}
-
- return CreateJ::Set(jstrs);
+ POP_AND_RETURN_NOTHING();
}
-jobject CreateJ::PropertyMap(apr_hash_t *prop_hash)
+jobject property_map(apr_hash_t *prop_hash, apr_array_header_t* prop_diffs,
+ apr_pool_t* scratch_pool)
{
- JNIEnv *env = JNIUtil::getEnv();
+ SVN_ERR_ASSERT_NO_RETURN(!(prop_hash && prop_diffs));
- if (prop_hash == NULL)
+ if (prop_hash == NULL && prop_diffs == NULL)
return NULL;
+ JNIEnv *env = JNIUtil::getEnv();
+
// Create a local frame for our references
env->PushLocalFrame(LOCAL_FRAME_SIZE);
if (JNIUtil::isJavaExceptionThrown())
@@ -1058,35 +1261,35 @@ jobject CreateJ::PropertyMap(apr_hash_t *prop_hash)
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
- apr_hash_index_t *hi;
- for (hi = apr_hash_first(apr_hash_pool_get(prop_hash), prop_hash);
- hi; hi = apr_hash_next(hi))
- {
- const char *key;
- svn_string_t *val;
-
- apr_hash_this(hi,
- reinterpret_cast<const void **>(&key),
- NULL,
- reinterpret_cast<void **>(&val));
+ fill_property_map(map, prop_hash, prop_diffs, scratch_pool, put_mid);
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
- jstring jpropName = JNIUtil::makeJString(key);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
+ return env->PopLocalFrame(map);
+}
+} // anonymous namespace
- jbyteArray jpropVal = JNIUtil::makeJByteArray(val);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
+jobject CreateJ::PropertyMap(apr_hash_t *prop_hash, apr_pool_t* scratch_pool)
+{
+ return property_map(prop_hash, NULL, scratch_pool);
+}
- env->CallObjectMethod(map, put_mid, jpropName, jpropVal);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
+jobject CreateJ::PropertyMap(apr_array_header_t* prop_diffs,
+ apr_pool_t* scratch_pool)
+{
+ return property_map(NULL, prop_diffs, scratch_pool);
+}
- env->DeleteLocalRef(jpropName);
- env->DeleteLocalRef(jpropVal);
- }
+void CreateJ::FillPropertyMap(jobject map, apr_hash_t* prop_hash,
+ apr_pool_t* scratch_pool, jmethodID put_mid)
+{
+ fill_property_map(map, prop_hash, NULL, scratch_pool, put_mid);
+}
- return env->PopLocalFrame(map);
+void CreateJ::FillPropertyMap(jobject map, apr_array_header_t* prop_diffs,
+ apr_pool_t* scratch_pool, jmethodID put_mid)
+{
+ fill_property_map(map, NULL, prop_diffs, scratch_pool, put_mid);
}
jobject CreateJ::InheritedProps(apr_array_header_t *iprops)
@@ -1123,7 +1326,7 @@ jobject CreateJ::InheritedProps(apr_array_header_t *iprops)
}
jclass item_cls = env->FindClass(
- JAVA_PACKAGE"/callback/InheritedProplistCallback$InheritedItem");
+ JAVAHL_CLASS("/callback/InheritedProplistCallback$InheritedItem"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NULL;
@@ -1169,6 +1372,64 @@ jobject CreateJ::InheritedProps(apr_array_header_t *iprops)
return env->PopLocalFrame(array);
}
+jobject CreateJ::Mergeinfo(svn_mergeinfo_t mergeinfo, apr_pool_t* scratch_pool)
+{
+ if (mergeinfo == NULL)
+ return NULL;
+
+ // Transform mergeinfo into Java Mergeinfo object.
+ JNIEnv *env = JNIUtil::getEnv();
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/types/Mergeinfo"));
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+
+ static jmethodID ctor = 0;
+ if (ctor == 0)
+ {
+ ctor = env->GetMethodID(clazz, "<init>", "()V");
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+ }
+
+ static jmethodID addRevisions = 0;
+ if (addRevisions == 0)
+ {
+ addRevisions = env->GetMethodID(clazz, "addRevisions",
+ "(Ljava/lang/String;"
+ "Ljava/util/List;)V");
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+ }
+
+ jobject jmergeinfo = env->NewObject(clazz, ctor);
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+
+ apr_hash_index_t *hi;
+ for (hi = apr_hash_first(scratch_pool, mergeinfo);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ const void *path;
+ void *val;
+ apr_hash_this(hi, &path, NULL, &val);
+
+ jstring jpath =
+ JNIUtil::makeJString(static_cast<const char*>(path));
+ jobject jranges =
+ RevisionRangeList(static_cast<svn_rangelist_t*>(val)).toList();
+
+ env->CallVoidMethod(jmergeinfo, addRevisions, jpath, jranges);
+
+ env->DeleteLocalRef(jranges);
+ env->DeleteLocalRef(jpath);
+ }
+
+ env->DeleteLocalRef(clazz);
+
+ return jmergeinfo;
+}
+
jobject CreateJ::Set(std::vector<jobject> &objects)
{
diff --git a/subversion/bindings/javahl/native/CreateJ.h b/subversion/bindings/javahl/native/CreateJ.h
index 7fe5363..8f4f36e 100644
--- a/subversion/bindings/javahl/native/CreateJ.h
+++ b/subversion/bindings/javahl/native/CreateJ.h
@@ -31,6 +31,7 @@
#include "svn_wc.h"
#include "svn_repos.h"
#include "svn_client.h"
+#include "svn_mergeinfo.h"
#include <vector>
@@ -49,12 +50,19 @@ class CreateJ
Checksum(const svn_checksum_t *checksum);
static jobject
+ DirEntry(const char *path, const char *absPath,
+ const svn_dirent_t *dirent);
+
+ static jobject
Info(const char *path, const svn_client_info2_t *info);
static jobject
Lock(const svn_lock_t *lock);
static jobject
+ LockMap(const apr_hash_t *locks, apr_pool_t *pool);
+
+ static jobject
ChangedPath(const char *path, svn_log_changed_path2_t *log_item);
static jobject
@@ -74,17 +82,30 @@ class CreateJ
CommitInfo(const svn_commit_info_t *info);
static jobject
- RevisionRangeList(svn_rangelist_t *ranges);
+ StringSet(const apr_array_header_t *strings);
static jobject
- StringSet(apr_array_header_t *strings);
+ PropertyMap(apr_hash_t *prop_hash, apr_pool_t* scratch_pool = NULL);
static jobject
- PropertyMap(apr_hash_t *prop_hash);
+ PropertyMap(apr_array_header_t* prop_diffs, apr_pool_t* scratch_pool = NULL);
+
+ static void
+ FillPropertyMap(jobject map, apr_hash_t* prop_hash,
+ apr_pool_t* scratch_pool,
+ jmethodID put_method_id = 0);
+
+ static void
+ FillPropertyMap(jobject map, apr_array_header_t* prop_diffs,
+ apr_pool_t* scratch_pool,
+ jmethodID put_method_id = 0);
static jobject
InheritedProps(apr_array_header_t *inherited_props);
+ static jobject
+ Mergeinfo(svn_mergeinfo_t mergeinfo, apr_pool_t* scratch_pool);
+
/* This creates a set of Objects. It derefs the members of the vector
* after putting them in the set, so they caller doesn't need to. */
static jobject
diff --git a/subversion/bindings/javahl/native/Credential.cpp b/subversion/bindings/javahl/native/Credential.cpp
new file mode 100644
index 0000000..8791de3
--- /dev/null
+++ b/subversion/bindings/javahl/native/Credential.cpp
@@ -0,0 +1,85 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#include "Credential.hpp"
+
+#include "JNIUtil.h"
+
+namespace JavaHL {
+
+// Class JavaHL::Credential
+const char* const Credential::m_class_name =
+ JAVAHL_CLASS("/SVNUtil$Credential");
+
+Credential::ClassImpl::ClassImpl(::Java::Env env, jclass cls)
+ : ::Java::Object::ClassImpl(env, cls),
+ m_mid_ctor(
+ env.GetMethodID(cls, "<init>",
+ "(" JAVAHL_ARG("/SVNUtil$Credential$Kind;")
+ "Ljava/lang/String;Ljava/lang/String;"
+ "Ljava/lang/String;Ljava/lang/String;"
+ JAVAHL_ARG("/callback/AuthnCallback$SSLServerCertInfo;")
+ JAVAHL_ARG("/callback/AuthnCallback$SSLServerCertFailures;")
+ "Ljava/lang/String;)V"))
+{}
+
+Credential::ClassImpl::~ClassImpl() {}
+
+Credential::Credential(::Java::Env env, jobject kind,
+ const ::Java::String& realm,
+ const ::Java::String& store,
+ const ::Java::String& username,
+ const ::Java::String& password,
+ jobject info, jobject failures,
+ const ::Java::String& passphrase)
+ : ::Java::Object(env, ::Java::ClassCache::get_credential(env))
+{
+ set_this(env.NewObject(get_class(), impl().m_mid_ctor,
+ kind, realm.get(), store.get(),
+ username.get(), password.get(),
+ info, failures, passphrase.get()));
+}
+
+// Enum JavaHL::Credential::Kind
+const char* const Credential::Kind::m_class_name =
+ JAVAHL_CLASS("/SVNUtil$Credential$Kind");
+
+Credential::Kind::ClassImpl::ClassImpl(::Java::Env env, jclass cls)
+ : ::Java::Object::ClassImpl(env, cls),
+ m_static_mid_from_string(
+ env.GetStaticMethodID(cls, "fromString",
+ "(Ljava/lang/String;)"
+ JAVAHL_ARG("/SVNUtil$Credential$Kind;")))
+{}
+
+Credential::Kind::ClassImpl::~ClassImpl() {}
+
+Credential::Kind::Kind(::Java::Env env,
+ const ::Java::String& value)
+ : ::Java::Object(env, ::Java::ClassCache::get_credential_kind(env))
+{
+ set_this(env.CallStaticObjectMethod(
+ get_class(), impl().m_static_mid_from_string, value.get()));
+}
+
+} // namespace JavaHL
diff --git a/subversion/bindings/javahl/native/Credential.hpp b/subversion/bindings/javahl/native/Credential.hpp
new file mode 100644
index 0000000..5c0c66d
--- /dev/null
+++ b/subversion/bindings/javahl/native/Credential.hpp
@@ -0,0 +1,126 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#ifndef SVN_JAVAHL_CREDENTIAL_HPP
+#define SVN_JAVAHL_CREDENTIAL_HPP
+
+#include "jniwrapper/jni_object.hpp"
+#include "jniwrapper/jni_string.hpp"
+
+#include "AuthnCallback.hpp"
+
+namespace JavaHL {
+
+/**
+ * Object wrapper for @c org.apache.subversion.javahl.SVNUtil.Credential.
+ *
+ * @since New in 1.9.
+ */
+class Credential : public ::Java::Object
+{
+public:
+ /**
+ * Object wrapper for @c ...Credential$Kind.
+ */
+ class Kind : public ::Java::Object
+ {
+ public:
+ /**
+ * Constructs a and wraps a new Credential$Kind object.
+ */
+ explicit Kind(::Java::Env env, const ::Java::String& value);
+
+ private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ::Java::ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(::Java::Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const ::Java::MethodID m_static_mid_from_string;
+ };
+
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+
+ friend class ::Java::ClassCacheImpl;
+ static const char* const m_class_name;
+ };
+
+ /**
+ * Constructs a wrapper around @a jthis.
+ * The constructor does not verify the class of the wrapped object.
+ */
+ explicit Credential(::Java::Env env, jobject jthis)
+ : ::Java::Object(env, ::Java::ClassCache::get_credential(env), jthis)
+ {}
+
+ /**
+ * Constructs and wraps a new Credential object
+ */
+ explicit Credential(::Java::Env env, jobject kind,
+ const ::Java::String& realm,
+ const ::Java::String& store,
+ const ::Java::String& username,
+ const ::Java::String& password,
+ jobject info, jobject failures,
+ const ::Java::String& passphrase);
+
+private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ::Java::ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(::Java::Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const ::Java::MethodID m_mid_ctor;
+ };
+
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+
+ friend class ::Java::ClassCacheImpl;
+ static const char* const m_class_name;
+};
+
+} // namespace JavaHL
+
+#endif // SVN_JAVAHL_CREDENTIAL_HPP
diff --git a/subversion/bindings/javahl/native/DiffOptions.cpp b/subversion/bindings/javahl/native/DiffOptions.cpp
index 8d7a8eb..f666f3a 100644
--- a/subversion/bindings/javahl/native/DiffOptions.cpp
+++ b/subversion/bindings/javahl/native/DiffOptions.cpp
@@ -71,5 +71,29 @@ apr_array_header_t *DiffOptions::optionsArray(SVN::Pool &resultPool) const
if (flags & SHOW_C_FUNCTION)
APR_ARRAY_PUSH(opt, const char*) = "--show-c-function";
+ /* TODO: Support -U (context size) */
+
+ return opt;
+}
+
+svn_diff_file_options_t *DiffOptions::fileOptions(SVN::Pool &resultPool) const
+{
+ svn_diff_file_options_t *opt;
+
+ opt = svn_diff_file_options_create(resultPool.getPool());
+
+ if (flags & IGNORE_ALL_SPACE)
+ opt->ignore_space = svn_diff_file_ignore_space_all;
+ else if (flags & IGNORE_SPACE_CHANGE)
+ opt->ignore_eol_style = svn_diff_file_ignore_space_change;
+
+ if (flags & IGNORE_EOL_STYLE)
+ opt->ignore_eol_style = TRUE;
+
+ if (flags & SHOW_C_FUNCTION)
+ opt->show_c_function = TRUE;
+
+ /* TODO: Support context size */
+
return opt;
}
diff --git a/subversion/bindings/javahl/native/DiffOptions.h b/subversion/bindings/javahl/native/DiffOptions.h
index e59b2f9..0dc4d9a 100644
--- a/subversion/bindings/javahl/native/DiffOptions.h
+++ b/subversion/bindings/javahl/native/DiffOptions.h
@@ -29,6 +29,7 @@
#include <apr_tables.h>
#include "svn_types.h"
+#include "svn_diff.h"
#include "Pool.h"
#include "JNIUtil.h"
@@ -38,6 +39,7 @@ class DiffOptions
DiffOptions(jobject joptions);
apr_array_header_t *optionsArray(SVN::Pool &resultPool) const;
+ svn_diff_file_options_t *fileOptions(SVN::Pool &resultPool) const;
svn_boolean_t useGitDiffFormat() const
{
diff --git a/subversion/bindings/javahl/native/DiffSummaryReceiver.cpp b/subversion/bindings/javahl/native/DiffSummaryReceiver.cpp
index f4cf052..8ad3a91 100644
--- a/subversion/bindings/javahl/native/DiffSummaryReceiver.cpp
+++ b/subversion/bindings/javahl/native/DiffSummaryReceiver.cpp
@@ -69,12 +69,12 @@ DiffSummaryReceiver::onSummary(const svn_client_diff_summarize_t *diff,
if (callback == 0)
{
// Initialize the method ID.
- clazz = env->FindClass(JAVA_PACKAGE "/callback/DiffSummaryCallback");
+ clazz = env->FindClass(JAVAHL_CLASS("/callback/DiffSummaryCallback"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN(SVN_NO_ERROR);
callback = env->GetMethodID(clazz, "onSummary",
- "(L"JAVA_PACKAGE"/DiffSummary;)V");
+ "(" JAVAHL_ARG("/DiffSummary;") ")V");
if (JNIUtil::isJavaExceptionThrown() || callback == 0)
POP_AND_RETURN(SVN_NO_ERROR);
}
@@ -82,7 +82,7 @@ DiffSummaryReceiver::onSummary(const svn_client_diff_summarize_t *diff,
// Do some prep work for tranforming the DIFF parameter into a
// Java equivalent.
static jmethodID ctor = 0;
- clazz = env->FindClass(JAVA_PACKAGE "/DiffSummary");
+ clazz = env->FindClass(JAVAHL_CLASS("/DiffSummary"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN(SVN_NO_ERROR);
@@ -90,8 +90,8 @@ DiffSummaryReceiver::onSummary(const svn_client_diff_summarize_t *diff,
{
ctor = env->GetMethodID(clazz, "<init>",
"(Ljava/lang/String;"
- "L"JAVA_PACKAGE"/DiffSummary$DiffKind;Z"
- "L"JAVA_PACKAGE"/types/NodeKind;)V");
+ JAVAHL_ARG("/DiffSummary$DiffKind;") "Z"
+ JAVAHL_ARG("/types/NodeKind;") ")V");
if (JNIUtil::isJavaExceptionThrown() || ctor == 0)
POP_AND_RETURN(SVN_NO_ERROR);
}
@@ -117,8 +117,5 @@ DiffSummaryReceiver::onSummary(const svn_client_diff_summarize_t *diff,
// Invoke the Java DiffSummaryReceiver callback.
env->CallVoidMethod(m_receiver, callback, jDiffSummary);
- // We return whether an exception was thrown or not.
-
- env->PopLocalFrame(NULL);
- return SVN_NO_ERROR;
+ POP_AND_RETURN_EXCEPTION_AS_SVNERROR();
}
diff --git a/subversion/bindings/javahl/native/EditorCallbacks.cpp b/subversion/bindings/javahl/native/EditorCallbacks.cpp
new file mode 100644
index 0000000..703f6bd
--- /dev/null
+++ b/subversion/bindings/javahl/native/EditorCallbacks.cpp
@@ -0,0 +1,124 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#include "EditorCallbacks.hpp"
+
+#include "jniwrapper/jni_stack.hpp"
+#include "jniwrapper/jni_string.hpp"
+#include "jniwrapper/jni_io_stream.hpp"
+
+#include "Utility.hpp"
+
+namespace JavaHL {
+
+// class JavaHL::ProvideBaseCallback
+
+const char* const ProvideBaseCallback::m_class_name =
+ JAVAHL_CLASS("/ISVNEditor$ProvideBaseCallback");
+
+ProvideBaseCallback::ClassImpl::ClassImpl(::Java::Env env, jclass cls)
+ : ::Java::Object::ClassImpl(env, cls),
+ m_mid_get_contents(
+ env.GetMethodID(
+ cls, "getContents",
+ "(Ljava/lang/String;)"
+ JAVAHL_ARG("/ISVNEditor$ProvideBaseCallback$ReturnValue;")))
+{}
+
+ProvideBaseCallback::ClassImpl::~ClassImpl() {}
+
+
+const char* const ProvideBaseCallback::ReturnValue::m_class_name =
+ JAVAHL_CLASS("/ISVNEditor$ProvideBaseCallback$ReturnValue");
+
+ProvideBaseCallback::ReturnValue::ClassImpl::ClassImpl(
+ ::Java::Env env, jclass cls)
+ : ::Java::Object::ClassImpl(env, cls),
+ m_fid_contents(env.GetFieldID(cls, "contents", "Ljava/io/InputStream;")),
+ m_fid_revision(env.GetFieldID(cls, "revision", "J"))
+{}
+
+ProvideBaseCallback::ReturnValue::ClassImpl::~ClassImpl() {}
+
+
+svn_stream_t*
+ProvideBaseCallback::ReturnValue::get_global_stream(apr_pool_t* pool) const
+{
+ jobject jstream = m_env.GetObjectField(m_jthis, impl().m_fid_contents);
+ return ::Java::InputStream::get_global_stream(m_env, jstream, pool);
+}
+
+
+// class JavaHL::ProvidePropsCallback
+
+const char* const ProvidePropsCallback::m_class_name =
+ JAVAHL_CLASS("/ISVNEditor$ProvidePropsCallback");
+
+ProvidePropsCallback::ClassImpl::ClassImpl(::Java::Env env, jclass cls)
+ : ::Java::Object::ClassImpl(env, cls),
+ m_mid_get_props(
+ env.GetMethodID(
+ cls, "getProperties",
+ "(Ljava/lang/String;)"
+ JAVAHL_ARG("/ISVNEditor$ProvidePropsCallback$ReturnValue;")))
+{}
+
+ProvidePropsCallback::ClassImpl::~ClassImpl() {}
+
+
+const char* const ProvidePropsCallback::ReturnValue::m_class_name =
+ JAVAHL_CLASS("/ISVNEditor$ProvidePropsCallback$ReturnValue");
+
+ProvidePropsCallback::ReturnValue::ClassImpl::ClassImpl(
+ ::Java::Env env, jclass cls)
+ : ::Java::Object::ClassImpl(env, cls),
+ m_fid_properties(env.GetFieldID(cls, "properties", "Ljava/util/Map;")),
+ m_fid_revision(env.GetFieldID(cls, "revision", "J"))
+{}
+
+ProvidePropsCallback::ReturnValue::ClassImpl::~ClassImpl() {}
+
+apr_hash_t*
+ProvidePropsCallback::ReturnValue::get_property_hash(apr_pool_t* pool) const
+{
+ jobject jproperties = m_env.GetObjectField(m_jthis, impl().m_fid_properties);
+ return Util::make_property_hash(m_env, jproperties, pool);
+}
+
+
+// class JavaHL::GetNodeKindCallback
+
+const char* const GetNodeKindCallback::m_class_name =
+ JAVAHL_CLASS("/ISVNEditor$GetNodeKindCallback");
+
+GetNodeKindCallback::ClassImpl::ClassImpl(::Java::Env env, jclass cls)
+ : ::Java::Object::ClassImpl(env, cls),
+ m_mid_get_kind(
+ env.GetMethodID(cls,
+ "getKind",
+ "(Ljava/lang/String;J)"
+ JAVAHL_ARG("/types/NodeKind;")))
+{}
+
+GetNodeKindCallback::ClassImpl::~ClassImpl() {}
+} // namespace JavaHL
diff --git a/subversion/bindings/javahl/native/EditorCallbacks.hpp b/subversion/bindings/javahl/native/EditorCallbacks.hpp
new file mode 100644
index 0000000..8918ee3
--- /dev/null
+++ b/subversion/bindings/javahl/native/EditorCallbacks.hpp
@@ -0,0 +1,328 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#ifndef SVN_JAVAHL_EDITOR_CALLBACKS_HPP
+#define SVN_JAVAHL_EDITOR_CALLBACKS_HPP
+
+#include "svn_io.h"
+
+#include "Pool.h"
+
+#include "jniwrapper/jni_object.hpp"
+
+namespace JavaHL {
+
+/**
+ * Object wrapper for the base contents callback interface in
+ * @c org.apache.subversion.javahl.ISVNEditor.
+ *
+ * @since New in 1.9.
+ */
+class ProvideBaseCallback : public ::Java::Object
+{
+public:
+ /**
+ * Constructs a wrapper around @a jthis.
+ * The constructor does not verify the class of the wrapped object.
+ */
+ explicit ProvideBaseCallback(::Java::Env env, jobject jthis)
+ : ::Java::Object(env,
+ ::Java::ClassCache::get_editor_provide_base_cb(env),
+ jthis)
+ {}
+
+ /**
+ * Invokes the callback.
+ */
+ jobject operator()(jstring relpath) const
+ {
+ return m_env.CallObjectMethod(m_jthis, impl().m_mid_get_contents,
+ relpath);
+ }
+
+ class ReturnValue : public ::Java::Object
+ {
+ public:
+ /**
+ * Constructs a wrapper around @a jthis.
+ * The constructor does not verify the class of the wrapped object.
+ */
+ explicit ReturnValue(::Java::Env env, jobject jthis)
+ : Java::Object(env,
+ ::Java::ClassCache::get_editor_provide_base_cb_ret(env),
+ jthis)
+ {}
+
+ /**
+ * Returns an @c svn_stream_t for the contents stream in the
+ * wrapped return value, allocated from @a pool. The wrapped Java
+ * stream will live as long as @a pool.
+ */
+ svn_stream_t* get_global_stream(apr_pool_t* pool) const;
+
+ /**
+ * Returns an @c svn_stream_t for the contents stream in the
+ * wrapped return value, allocated from @a pool. The wrapped Java
+ * stream will live as long as @a pool.
+ */
+ svn_stream_t* get_global_stream(const SVN::Pool& pool) const
+ {
+ return get_global_stream(pool.getPool());
+ }
+
+ /**
+ * Returns the revision in the wrapped return value.
+ */
+ jlong get_revision() const
+ {
+ return m_env.GetLongField(m_jthis, impl().m_fid_revision);
+ }
+
+ private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ::Java::ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(::Java::Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const ::Java::FieldID m_fid_contents;
+ const ::Java::FieldID m_fid_revision;
+ };
+
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+
+ friend class ::Java::ClassCacheImpl;
+ static const char* const m_class_name;
+ };
+
+private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ::Java::ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(::Java::Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const ::Java::MethodID m_mid_get_contents;
+ };
+
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+
+ friend class ::Java::ClassCacheImpl;
+ static const char* const m_class_name;
+};
+
+
+/**
+ * Object wrapper for the props callback interface in
+ * @c org.apache.subversion.javahl.ISVNEditor.
+ *
+ * @since New in 1.9.
+ */
+class ProvidePropsCallback : public ::Java::Object
+{
+public:
+ /**
+ * Constructs a wrapper around @a jthis.
+ * The constructor does not verify the class of the wrapped object.
+ */
+ explicit ProvidePropsCallback(::Java::Env env, jobject jthis)
+ : ::Java::Object(env,
+ ::Java::ClassCache::get_editor_provide_props_cb(env),
+ jthis)
+ {}
+
+ /**
+ * Invokes the callback.
+ */
+ jobject operator()(jstring relpath) const
+ {
+ return m_env.CallObjectMethod(m_jthis, impl().m_mid_get_props, relpath);
+ }
+
+ class ReturnValue : public ::Java::Object
+ {
+ public:
+ /**
+ * Constructs a wrapper around @a jthis.
+ * The constructor does not verify the class of the wrapped object.
+ */
+ explicit ReturnValue(::Java::Env env, jobject jthis)
+ : Java::Object(env,
+ ::Java::ClassCache::get_editor_provide_props_cb_ret(env),
+ jthis)
+ {}
+
+ /**
+ * Returns an @c apr_hash_t of the node properties in the wrapped
+ * return value, allocated from @a pool.
+ */
+ apr_hash_t* get_property_hash(apr_pool_t* pool) const;
+
+ /**
+ * Returns an @c apr_hash_t of the node properties in the wrapped
+ * return value, allocated from @a pool.
+ */
+ apr_hash_t* get_property_hash(const SVN::Pool& pool) const
+ {
+ return get_property_hash(pool.getPool());
+ }
+
+ /**
+ * Returns the revision in the wrapped return value.
+ */
+ jlong get_revision() const
+ {
+ return m_env.GetLongField(m_jthis, impl().m_fid_revision);
+ }
+
+ private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ::Java::ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(::Java::Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const ::Java::FieldID m_fid_properties;
+ const ::Java::FieldID m_fid_revision;
+ };
+
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+
+ friend class ::Java::ClassCacheImpl;
+ static const char* const m_class_name;
+ };
+
+private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ::Java::ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(::Java::Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const ::Java::MethodID m_mid_get_props;
+ };
+
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+
+ friend class ::Java::ClassCacheImpl;
+ static const char* const m_class_name;
+};
+
+
+/**
+ * Object wrapper for the kind callback interface in
+ * @c org.apache.subversion.javahl.ISVNEditor.
+ *
+ * @since New in 1.9.
+ */
+class GetNodeKindCallback : public ::Java::Object
+{
+public:
+ /**
+ * Constructs a wrapper around @a jthis.
+ * The constructor does not verify the class of the wrapped object.
+ */
+ explicit GetNodeKindCallback(::Java::Env env, jobject jthis)
+ : ::Java::Object(env,
+ ::Java::ClassCache::get_editor_get_kind_cb(env),
+ jthis)
+ {}
+
+ /**
+ * Invokes the callback.
+ */
+ jobject operator()(jstring relpath, jlong revision) const
+ {
+ return m_env.CallObjectMethod(m_jthis, impl().m_mid_get_kind,
+ relpath, revision);
+ }
+
+private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ::Java::ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(::Java::Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const ::Java::MethodID m_mid_get_kind;
+ };
+
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+
+ friend class ::Java::ClassCacheImpl;
+ static const char* const m_class_name;
+};
+
+} // namespace JavaHL
+
+#endif // SVN_JAVAHL_EDITOR_CALLBACKS_HPP
diff --git a/subversion/bindings/javahl/native/EditorProxy.cpp b/subversion/bindings/javahl/native/EditorProxy.cpp
new file mode 100644
index 0000000..8d9d65e
--- /dev/null
+++ b/subversion/bindings/javahl/native/EditorProxy.cpp
@@ -0,0 +1,566 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file EditorProxy.h
+ * @brief Interface of all editor proxy classes
+ */
+#include <apr_pools.h>
+
+#include "JNIUtil.h"
+#include "JNIStackElement.h"
+#include "EditorProxy.h"
+#include "CreateJ.h"
+#include "EnumMapper.h"
+
+// Newstyle: stream wrapper
+#include <memory>
+#include "NativeStream.hpp"
+#include "jniwrapper/jni_stack.hpp"
+
+#include "svn_error.h"
+#include "svn_private_config.h"
+
+EditorProxy::EditorProxy(jobject jeditor, apr_pool_t* edit_pool,
+ const char* repos_root_url, const char* base_relpath,
+ svn_cancel_func_t cancel_func, void* cancel_baton,
+ const EditorProxyCallbacks& callbacks)
+ : m_valid(false),
+ m_jeditor(JNIUtil::getEnv()->NewGlobalRef(jeditor)),
+ m_edit_pool(edit_pool),
+ m_repos_root_url(NULL),
+ m_base_relpath(NULL),
+ m_found_paths(false),
+ m_editor(NULL),
+ m_delta_editor(NULL),
+ m_delta_baton(NULL),
+ m_proxy_callbacks(callbacks)
+{
+ //DEBUG:fprintf(stderr, " (n) EditorProxy::EditorProxy(...)\n");
+
+ static const svn_editor_cb_many_t editor_many_cb = {
+ cb_add_directory, cb_add_file, cb_add_symlink, cb_add_absent,
+ cb_alter_directory, cb_alter_file, cb_alter_symlink,
+ cb_delete, cb_copy, cb_move,
+ cb_complete, cb_abort
+ };
+
+ SVN::Pool scratchPool(edit_pool);
+ apr_pool_t* scratch_pool = scratchPool.getPool();
+
+ svn_error_t* err = svn_editor_create(&m_editor, this,
+ cancel_func, cancel_baton,
+ edit_pool, scratch_pool);
+ if (!err)
+ err = svn_editor_setcb_many(m_editor, &editor_many_cb, scratch_pool);
+ if (!err)
+ {
+ m_repos_root_url =
+ static_cast<const char*>(apr_pstrdup(edit_pool, repos_root_url));
+ m_base_relpath =
+ static_cast<const char*>(apr_pstrdup(edit_pool, base_relpath));
+
+ svn_boolean_t found_paths;
+ err = svn_delta__delta_from_editor(&m_delta_editor,
+ &m_delta_baton,
+ m_editor,
+ m_proxy_callbacks.m_unlock_func,
+ m_proxy_callbacks.m_baton,
+ &found_paths,
+ repos_root_url, base_relpath,
+ m_proxy_callbacks.m_fetch_props_func,
+ m_proxy_callbacks.m_baton,
+ m_proxy_callbacks.m_fetch_base_func,
+ m_proxy_callbacks.m_baton,
+ &m_proxy_callbacks.m_extra_baton,
+ edit_pool);
+ m_found_paths = found_paths;
+ }
+
+ if (err)
+ JNIUtil::handleSVNError(err);
+ else
+ m_valid = true;
+}
+
+EditorProxy::~EditorProxy()
+{
+ //DEBUG:fprintf(stderr, " (n) EditorProxy::~EditorProxy()\n");
+
+ if (m_jeditor)
+ JNIUtil::getEnv()->DeleteGlobalRef(m_jeditor);
+}
+
+namespace {
+inline svn_error_t* invalid_editor()
+{
+ return svn_error_create(SVN_ERR_RA_SVN_EDIT_ABORTED, NULL,
+ _("The editor is not valid"));
+}
+
+svn_error_t*
+get_editor_method(jmethodID& mid, const char* name, const char* sig)
+{
+ if (0 != mid)
+ return SVN_NO_ERROR; // Already known.
+
+ JNIEnv* env = JNIUtil::getEnv();
+ jclass cls = env->FindClass(JAVAHL_CLASS("/ISVNEditor"));
+ SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED);
+ SVN_JNI_CATCH(mid = env->GetMethodID(cls, name, sig),
+ SVN_ERR_RA_SVN_EDIT_ABORTED);
+ return SVN_NO_ERROR;
+}
+
+jobject wrap_input_stream(svn_stream_t* stream)
+{
+ std::auto_ptr<JavaHL::NativeInputStream>
+ wrapped(new JavaHL::NativeInputStream());
+ apr_pool_t* const wrapped_pool = wrapped->get_pool().getPool();
+ wrapped->set_stream(svn_stream_disown(stream, wrapped_pool));
+ const jobject jstream = wrapped->create_java_wrapper();
+ wrapped.release();
+ return jstream;
+}
+} // anonymous namespace
+
+svn_error_t*
+EditorProxy::cb_add_directory(void *baton,
+ const char *relpath,
+ const apr_array_header_t *children,
+ apr_hash_t *props,
+ svn_revnum_t replaces_rev,
+ apr_pool_t *scratch_pool)
+{
+ //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_add_directory('%s')\n", relpath);
+ const ::Java::Env env;
+ SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED,
+ {
+ ::Java::LocalFrame frame(env);
+
+ EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+ if (!ep->m_valid)
+ return invalid_editor();
+
+ static jmethodID mid = 0;
+ SVN_ERR(get_editor_method(mid, "addDirectory",
+ "(Ljava/lang/String;"
+ "Ljava/lang/Iterable;"
+ "Ljava/util/Map;J)V"));
+
+ jstring jrelpath = JNIUtil::makeJString(relpath);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+ jobject jchildren = (!children ? NULL : CreateJ::StringSet(children));
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+ jobject jprops = CreateJ::PropertyMap(props, scratch_pool);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+
+ env.CallVoidMethod(ep->m_jeditor, mid,
+ jrelpath, jchildren, jprops,
+ jlong(replaces_rev));
+ });
+ return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_add_file(void *baton,
+ const char *relpath,
+ const svn_checksum_t *checksum,
+ svn_stream_t *contents,
+ apr_hash_t *props,
+ svn_revnum_t replaces_rev,
+ apr_pool_t *scratch_pool)
+{
+ //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_add_file('%s')\n", relpath);
+ const ::Java::Env env;
+ SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED,
+ {
+ ::Java::LocalFrame frame(env);
+
+ EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+ if (!ep || !ep->m_valid)
+ return invalid_editor();
+
+ static jmethodID mid = 0;
+ SVN_ERR(get_editor_method(mid, "addFile",
+ "(Ljava/lang/String;"
+ JAVAHL_ARG("/types/Checksum;")
+ "Ljava/io/InputStream;"
+ "Ljava/util/Map;J)V"));
+
+ jstring jrelpath = JNIUtil::makeJString(relpath);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+ jobject jchecksum = CreateJ::Checksum(checksum);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+ jobject jprops = CreateJ::PropertyMap(props, scratch_pool);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+
+ jobject jcontents = NULL;
+ if (contents != NULL)
+ jcontents = wrap_input_stream(contents);
+
+ env.CallVoidMethod(ep->m_jeditor, mid,
+ jrelpath, jchecksum, jcontents,
+ jprops, jlong(replaces_rev));
+ });
+ return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_add_symlink(void *baton,
+ const char *relpath,
+ const char *target,
+ apr_hash_t *props,
+ svn_revnum_t replaces_rev,
+ apr_pool_t *scratch_pool)
+{
+ //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_add_symlink('%s')\n", relpath);
+ const ::Java::Env env;
+ SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED,
+ {
+ ::Java::LocalFrame frame(env);
+
+ EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+ if (!ep || !ep->m_valid)
+ return invalid_editor();
+
+ static jmethodID mid = 0;
+ SVN_ERR(get_editor_method(mid, "addSymlink",
+ "(Ljava/lang/String;"
+ "Ljava/lang/String;"
+ "Ljava/util/Map;J)V"));
+
+ jstring jrelpath = JNIUtil::makeJString(relpath);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+ jstring jtarget = JNIUtil::makeJString(target);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+ jobject jprops = CreateJ::PropertyMap(props, scratch_pool);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+
+ env.CallVoidMethod(ep->m_jeditor, mid,
+ jrelpath, jtarget, jprops,
+ jlong(replaces_rev));
+ });
+ return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_add_absent(void *baton,
+ const char *relpath,
+ svn_node_kind_t kind,
+ svn_revnum_t replaces_rev,
+ apr_pool_t *scratch_pool)
+{
+ //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_add_absent('%s')\n", relpath);
+ const ::Java::Env env;
+ SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED,
+ {
+ ::Java::LocalFrame frame(env);
+
+ EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+ if (!ep || !ep->m_valid)
+ return invalid_editor();
+
+ static jmethodID mid = 0;
+ SVN_ERR(get_editor_method(mid, "addAbsent",
+ "(Ljava/lang/String;"
+ JAVAHL_ARG("/types/NodeKind;")
+ "J)V"));
+
+ jstring jrelpath = JNIUtil::makeJString(relpath);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+ jobject jkind = EnumMapper::mapNodeKind(kind);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+
+ env.CallVoidMethod(ep->m_jeditor, mid,
+ jrelpath, jkind,
+ jlong(replaces_rev));
+ });
+ return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_alter_directory(void *baton,
+ const char *relpath,
+ svn_revnum_t revision,
+ const apr_array_header_t *children,
+ apr_hash_t *props,
+ apr_pool_t *scratch_pool)
+{
+ //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_alter_directory('%s', r%lld)\n",
+ //DEBUG: relpath, static_cast<long long>(revision));
+ const ::Java::Env env;
+ SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED,
+ {
+ ::Java::LocalFrame frame(env);
+
+ EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+ if (!ep || !ep->m_valid)
+ return invalid_editor();
+
+ static jmethodID mid = 0;
+ SVN_ERR(get_editor_method(mid, "alterDirectory",
+ "(Ljava/lang/String;J"
+ "Ljava/lang/Iterable;"
+ "Ljava/util/Map;)V"));
+
+ jstring jrelpath = JNIUtil::makeJString(relpath);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+ jobject jchildren = (!children ? NULL : CreateJ::StringSet(children));
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+ jobject jprops = CreateJ::PropertyMap(props, scratch_pool);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+
+ env.CallVoidMethod(ep->m_jeditor, mid,
+ jrelpath, jlong(revision),
+ jchildren, jprops);
+ });
+ return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_alter_file(void *baton,
+ const char *relpath,
+ svn_revnum_t revision,
+ const svn_checksum_t *checksum,
+ svn_stream_t *contents,
+ apr_hash_t *props,
+ apr_pool_t *scratch_pool)
+{
+ //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_alter_file('%s', r%lld)\n",
+ //DEBUG: relpath, static_cast<long long>(revision));
+ const ::Java::Env env;
+ SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED,
+ {
+ ::Java::LocalFrame frame(env);
+
+ EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+ if (!ep || !ep->m_valid)
+ return invalid_editor();
+
+ static jmethodID mid = 0;
+ SVN_ERR(get_editor_method(mid, "alterFile",
+ "(Ljava/lang/String;J"
+ JAVAHL_ARG("/types/Checksum;")
+ "Ljava/io/InputStream;"
+ "Ljava/util/Map;)V"));
+
+ jstring jrelpath = JNIUtil::makeJString(relpath);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+ jobject jchecksum = CreateJ::Checksum(checksum);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+ jobject jprops = CreateJ::PropertyMap(props, scratch_pool);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+
+ jobject jcontents = NULL;
+ if (contents != NULL)
+ jcontents = wrap_input_stream(contents);
+
+ env.CallVoidMethod(ep->m_jeditor, mid,
+ jrelpath, jlong(revision),
+ jchecksum, jcontents, jprops);
+ });
+ return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_alter_symlink(void *baton,
+ const char *relpath,
+ svn_revnum_t revision,
+ const char *target,
+ apr_hash_t *props,
+ apr_pool_t *scratch_pool)
+{
+ //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_alter_symlink('%s', r%lld)\n",
+ //DEBUG: relpath, static_cast<long long>(revision));
+ const ::Java::Env env;
+ SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED,
+ {
+ ::Java::LocalFrame frame(env);
+
+ EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+ if (!ep || !ep->m_valid)
+ return invalid_editor();
+
+ static jmethodID mid = 0;
+ SVN_ERR(get_editor_method(mid, "alterSymlink",
+ "(Ljava/lang/String;J"
+ "Ljava/lang/String;"
+ "Ljava/util/Map;)V"));
+
+ jstring jrelpath = JNIUtil::makeJString(relpath);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+ jstring jtarget = JNIUtil::makeJString(target);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+ jobject jprops = CreateJ::PropertyMap(props, scratch_pool);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+
+ env.CallVoidMethod(ep->m_jeditor, mid,
+ jrelpath, jlong(revision),
+ jtarget, jprops);
+ });
+ return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_delete(void *baton,
+ const char *relpath,
+ svn_revnum_t revision,
+ apr_pool_t *scratch_pool)
+{
+ //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_delete('%s', r%lld)\n",
+ //DEBUG: relpath, static_cast<long long>(revision));
+ const ::Java::Env env;
+ SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED,
+ {
+ ::Java::LocalFrame frame(env);
+
+ EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+ if (!ep || !ep->m_valid)
+ return invalid_editor();
+
+ static jmethodID mid = 0;
+ SVN_ERR(get_editor_method(mid, "delete",
+ "(Ljava/lang/String;J)V"));
+
+ jstring jrelpath = JNIUtil::makeJString(relpath);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+
+ env.CallVoidMethod(ep->m_jeditor, mid, jrelpath);
+ });
+ return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_copy(void *baton,
+ const char *src_relpath,
+ svn_revnum_t src_revision,
+ const char *dst_relpath,
+ svn_revnum_t replaces_rev,
+ apr_pool_t *scratch_pool)
+{
+ //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_copy('%s', r%lld, '%s')\n",
+ //DEBUG: src_relpath, static_cast<long long>(src_revision), dst_relpath);
+ const ::Java::Env env;
+ SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED,
+ {
+ ::Java::LocalFrame frame(env);
+
+ EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+ if (!ep || !ep->m_valid)
+ return invalid_editor();
+
+ static jmethodID mid = 0;
+ SVN_ERR(get_editor_method(mid, "copy",
+ "(Ljava/lang/String;J"
+ "Ljava/lang/String;J)V"));
+
+ jstring jsrc_relpath = JNIUtil::makeJString(src_relpath);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+ jstring jdst_relpath = JNIUtil::makeJString(dst_relpath);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+
+ env.CallVoidMethod(ep->m_jeditor, mid,
+ jsrc_relpath, jlong(src_revision),
+ jdst_relpath, jlong(replaces_rev));
+ });
+ return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_move(void *baton,
+ const char *src_relpath,
+ svn_revnum_t src_revision,
+ const char *dst_relpath,
+ svn_revnum_t replaces_rev,
+ apr_pool_t *scratch_pool)
+{
+ //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_move('%s', r%lld, '%s')\n",
+ //DEBUG: src_relpath, static_cast<long long>(src_revision), dst_relpath);
+ const ::Java::Env env;
+ SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED,
+ {
+ ::Java::LocalFrame frame(env);
+
+ EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+ if (!ep || !ep->m_valid)
+ return invalid_editor();
+
+ static jmethodID mid = 0;
+ SVN_ERR(get_editor_method(mid, "move",
+ "(Ljava/lang/String;J"
+ "Ljava/lang/String;J)V"));
+
+ jstring jsrc_relpath = JNIUtil::makeJString(src_relpath);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+ jstring jdst_relpath = JNIUtil::makeJString(dst_relpath);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+
+ env.CallVoidMethod(ep->m_jeditor, mid,
+ jsrc_relpath, jlong(src_revision),
+ jdst_relpath, jlong(replaces_rev));
+ });
+ return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_complete(void *baton, apr_pool_t *scratch_pool)
+{
+ //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_complete()\n");
+ const ::Java::Env env;
+ SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED,
+ {
+ ::Java::LocalFrame frame(env);
+
+ EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+ if (!ep || !ep->m_valid)
+ return invalid_editor();
+ ep->m_valid = false;
+
+ static jmethodID mid = 0;
+ SVN_ERR(get_editor_method(mid, "complete", "()V"));
+
+ env.CallVoidMethod(ep->m_jeditor, mid);
+ });
+ return SVN_NO_ERROR;
+}
+
+svn_error_t*
+EditorProxy::cb_abort(void *baton, apr_pool_t *scratch_pool)
+{
+ //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_abort()\n");
+ const ::Java::Env env;
+ SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED,
+ {
+ ::Java::LocalFrame frame(env);
+
+ EditorProxy* const ep = static_cast<EditorProxy*>(baton);
+ if (!ep || !ep->m_valid)
+ return invalid_editor();
+ ep->m_valid = false;
+
+ static jmethodID mid = 0;
+ SVN_ERR(get_editor_method(mid, "abort", "()V"));
+
+ env.CallVoidMethod(ep->m_jeditor, mid);
+ });
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/bindings/javahl/native/EditorProxy.h b/subversion/bindings/javahl/native/EditorProxy.h
new file mode 100644
index 0000000..3ca005c
--- /dev/null
+++ b/subversion/bindings/javahl/native/EditorProxy.h
@@ -0,0 +1,153 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file EditorProxy.h
+ * @brief Interface of all editor proxy classes
+ */
+
+#ifndef JAVAHL_EDITOR_PROXY_H
+#define JAVAHL_EDITOR_PROXY_H
+
+#include "svn_delta.h"
+#include "private/svn_editor.h"
+#include "private/svn_delta_private.h"
+
+/**
+ * These callbacks are needed by the delta-to-Ev2 shims.
+ */
+struct EditorProxyCallbacks
+{
+ svn_delta__unlock_func_t m_unlock_func;
+ svn_delta_fetch_props_func_t m_fetch_props_func;
+ svn_delta_fetch_base_func_t m_fetch_base_func;
+ struct svn_delta__extra_baton m_extra_baton;
+ void* m_baton;
+};
+
+/**
+ * This is a proxy object that translates Ev2 operations (possibly
+ * implemented through shims) into calls to a Java editor
+ * implementation.
+ */
+class EditorProxy
+{
+public:
+ EditorProxy(jobject jeditor, apr_pool_t* edit_pool,
+ const char* repos_root_url, const char* base_relpath,
+ svn_cancel_func_t cancel_func, void* cancel_baton,
+ const EditorProxyCallbacks& callbacks);
+ ~EditorProxy();
+
+ const svn_delta_editor_t* delta_editor() const
+ {
+ return m_delta_editor;
+ }
+
+ void* delta_baton() const
+ {
+ return m_delta_baton;
+ }
+
+private:
+ EditorProxy(const EditorProxy&); // noncopyable
+
+ static svn_error_t* cb_add_directory(void *baton,
+ const char *relpath,
+ const apr_array_header_t *children,
+ apr_hash_t *props,
+ svn_revnum_t replaces_rev,
+ apr_pool_t *scratch_pool);
+ static svn_error_t* cb_add_file(void *baton,
+ const char *relpath,
+ const svn_checksum_t *checksum,
+ svn_stream_t *contents,
+ apr_hash_t *props,
+ svn_revnum_t replaces_rev,
+ apr_pool_t *scratch_pool);
+ static svn_error_t* cb_add_symlink(void *baton,
+ const char *relpath,
+ const char *target,
+ apr_hash_t *props,
+ svn_revnum_t replaces_rev,
+ apr_pool_t *scratch_pool);
+ static svn_error_t* cb_add_absent(void *baton,
+ const char *relpath,
+ svn_node_kind_t kind,
+ svn_revnum_t replaces_rev,
+ apr_pool_t *scratch_pool);
+ static svn_error_t* cb_alter_directory(void *baton,
+ const char *relpath,
+ svn_revnum_t revision,
+ const apr_array_header_t *children,
+ apr_hash_t *props,
+ apr_pool_t *scratch_pool);
+ static svn_error_t* cb_alter_file(void *baton,
+ const char *relpath,
+ svn_revnum_t revision,
+ const svn_checksum_t *checksum,
+ svn_stream_t *contents,
+ apr_hash_t *props,
+ apr_pool_t *scratch_pool);
+ static svn_error_t* cb_alter_symlink(void *baton,
+ const char *relpath,
+ svn_revnum_t revision,
+ const char *target,
+ apr_hash_t *props,
+ apr_pool_t *scratch_pool);
+ static svn_error_t* cb_delete(void *baton,
+ const char *relpath,
+ svn_revnum_t revision,
+ apr_pool_t *scratch_pool);
+ static svn_error_t* cb_copy(void *baton,
+ const char *src_relpath,
+ svn_revnum_t src_revision,
+ const char *dst_relpath,
+ svn_revnum_t replaces_rev,
+ apr_pool_t *scratch_pool);
+ static svn_error_t* cb_move(void *baton,
+ const char *src_relpath,
+ svn_revnum_t src_revision,
+ const char *dst_relpath,
+ svn_revnum_t replaces_rev,
+ apr_pool_t *scratch_pool);
+ static svn_error_t* cb_complete(void *baton,
+ apr_pool_t *scratch_pool);
+ static svn_error_t* cb_abort(void *baton,
+ apr_pool_t *scratch_pool);
+
+private:
+ bool m_valid;
+ jobject m_jeditor; ///< Reference to Java editor implementation
+ apr_pool_t* m_edit_pool;
+
+ const char* m_repos_root_url; ///< The root of the repository
+ const char* m_base_relpath; ///< The root of the session within the repo
+ bool m_found_paths; ///< Returned paths are absolute
+
+ svn_editor_t* m_editor;
+ const svn_delta_editor_t* m_delta_editor;
+ void* m_delta_baton;
+ EditorProxyCallbacks m_proxy_callbacks;
+};
+
+
+#endif // JAVAHL_EDITOR_PROXY_H
diff --git a/subversion/bindings/javahl/native/EnumMapper.cpp b/subversion/bindings/javahl/native/EnumMapper.cpp
index 089ef6f..8e8ca50 100644
--- a/subversion/bindings/javahl/native/EnumMapper.cpp
+++ b/subversion/bindings/javahl/native/EnumMapper.cpp
@@ -32,44 +32,18 @@
#include "JNIStringHolder.h"
#include "../include/org_apache_subversion_javahl_CommitItemStateFlags.h"
-/**
- * Map a C commit state flag constant to the Java constant.
- * @param state the C commit state flage constant
- * @returns the Java constant
- */
-jint EnumMapper::mapCommitMessageStateFlags(apr_byte_t flags)
-{
- jint jstateFlags = 0;
- if (flags & SVN_CLIENT_COMMIT_ITEM_ADD)
- jstateFlags |=
- org_apache_subversion_javahl_CommitItemStateFlags_Add;
- if (flags & SVN_CLIENT_COMMIT_ITEM_DELETE)
- jstateFlags |=
- org_apache_subversion_javahl_CommitItemStateFlags_Delete;
- if (flags & SVN_CLIENT_COMMIT_ITEM_TEXT_MODS)
- jstateFlags |=
- org_apache_subversion_javahl_CommitItemStateFlags_TextMods;
- if (flags & SVN_CLIENT_COMMIT_ITEM_PROP_MODS)
- jstateFlags |=
- org_apache_subversion_javahl_CommitItemStateFlags_PropMods;
- if (flags & SVN_CLIENT_COMMIT_ITEM_IS_COPY)
- jstateFlags |=
- org_apache_subversion_javahl_CommitItemStateFlags_IsCopy;
- return jstateFlags;
-}
-
jobject EnumMapper::mapChangePathAction(const char action)
{
switch (action)
{
case 'A':
- return mapEnum(JAVA_PACKAGE"/types/ChangePath$Action", 0);
+ return mapEnum(JAVAHL_CLASS("/types/ChangePath$Action"), 0);
case 'D':
- return mapEnum(JAVA_PACKAGE"/types/ChangePath$Action", 1);
+ return mapEnum(JAVAHL_CLASS("/types/ChangePath$Action"), 1);
case 'R':
- return mapEnum(JAVA_PACKAGE"/types/ChangePath$Action", 2);
+ return mapEnum(JAVAHL_CLASS("/types/ChangePath$Action"), 2);
case 'M':
- return mapEnum(JAVA_PACKAGE"/types/ChangePath$Action", 3);
+ return mapEnum(JAVAHL_CLASS("/types/ChangePath$Action"), 3);
default:
return NULL;
}
@@ -81,7 +55,7 @@ jobject EnumMapper::mapChangePathAction(const char action)
jobject EnumMapper::mapNotifyState(svn_wc_notify_state_t state)
{
// We're assuming a valid value for the C enum above
- return mapEnum(JAVA_PACKAGE"/ClientNotifyInformation$Status",
+ return mapEnum(JAVAHL_CLASS("/ClientNotifyInformation$Status"),
static_cast<int>(state));
}
@@ -91,14 +65,14 @@ jobject EnumMapper::mapNotifyState(svn_wc_notify_state_t state)
jobject EnumMapper::mapNotifyAction(svn_wc_notify_action_t action)
{
// We're assuming a valid value for the C enum above
- return mapEnum(JAVA_PACKAGE"/ClientNotifyInformation$Action",
+ return mapEnum(JAVAHL_CLASS("/ClientNotifyInformation$Action"),
static_cast<int>(action));
}
jobject EnumMapper::mapReposNotifyNodeAction(svn_node_action action)
{
// We're assuming a valid value for the C enum above
- return mapEnum(JAVA_PACKAGE"/ReposNotifyInformation$NodeAction",
+ return mapEnum(JAVAHL_CLASS("/ReposNotifyInformation$NodeAction"),
static_cast<int>(action));
}
@@ -108,7 +82,7 @@ jobject EnumMapper::mapReposNotifyNodeAction(svn_node_action action)
jobject EnumMapper::mapReposNotifyAction(svn_repos_notify_action_t action)
{
// We're assuming a valid value for the C enum above
- return mapEnum(JAVA_PACKAGE"/ReposNotifyInformation$Action",
+ return mapEnum(JAVAHL_CLASS("/ReposNotifyInformation$Action"),
static_cast<int>(action));
}
@@ -118,7 +92,7 @@ jobject EnumMapper::mapReposNotifyAction(svn_repos_notify_action_t action)
jobject EnumMapper::mapNodeKind(svn_node_kind_t nodeKind)
{
// We're assuming a valid value for the C enum above
- return mapEnum(JAVA_PACKAGE"/types/NodeKind",
+ return mapEnum(JAVAHL_CLASS("/types/NodeKind"),
static_cast<int>(nodeKind));
}
@@ -128,7 +102,7 @@ jobject EnumMapper::mapNodeKind(svn_node_kind_t nodeKind)
jobject EnumMapper::mapNotifyLockState(svn_wc_notify_lock_state_t state)
{
// We're assuming a valid value for the C enum above
- return mapEnum(JAVA_PACKAGE"/ClientNotifyInformation$LockStatus",
+ return mapEnum(JAVAHL_CLASS("/ClientNotifyInformation$LockStatus"),
static_cast<int>(state));
}
@@ -138,7 +112,7 @@ jobject EnumMapper::mapNotifyLockState(svn_wc_notify_lock_state_t state)
jobject EnumMapper::mapScheduleKind(svn_wc_schedule_t schedule)
{
// We're assuming a valid value for the C enum above
- return mapEnum(JAVA_PACKAGE"/types/Info$ScheduleKind",
+ return mapEnum(JAVAHL_CLASS("/types/Info$ScheduleKind"),
static_cast<int>(schedule));
}
@@ -149,91 +123,121 @@ jobject EnumMapper::mapStatusKind(svn_wc_status_kind svnKind)
{
// We're assuming a valid value for the C enum above
// The offset here is +1
- return mapEnum(JAVA_PACKAGE"/types/Status$Kind",
+ return mapEnum(JAVAHL_CLASS("/types/Status$Kind"),
static_cast<int>(svnKind) - 1);
}
jobject EnumMapper::mapChecksumKind(svn_checksum_kind_t kind)
{
// We're assuming a valid value for the C enum above
- return mapEnum(JAVA_PACKAGE"/types/Checksum$Kind",
+ return mapEnum(JAVAHL_CLASS("/types/Checksum$Kind"),
static_cast<int>(kind));
}
jobject EnumMapper::mapConflictKind(svn_wc_conflict_kind_t kind)
{
// We're assuming a valid value for the C enum above
- return mapEnum(JAVA_PACKAGE"/ConflictDescriptor$Kind",
+ return mapEnum(JAVAHL_CLASS("/ConflictDescriptor$Kind"),
static_cast<int>(kind));
}
jobject EnumMapper::mapConflictAction(svn_wc_conflict_action_t action)
{
// We're assuming a valid value for the C enum above
- return mapEnum(JAVA_PACKAGE"/ConflictDescriptor$Action",
+ return mapEnum(JAVAHL_CLASS("/ConflictDescriptor$Action"),
static_cast<int>(action));
}
jobject EnumMapper::mapConflictReason(svn_wc_conflict_reason_t reason)
{
// We're assuming a valid value for the C enum above
- return mapEnum(JAVA_PACKAGE"/ConflictDescriptor$Reason",
+ return mapEnum(JAVAHL_CLASS("/ConflictDescriptor$Reason"),
static_cast<int>(reason));
}
int EnumMapper::toMergeinfoLogKind(jobject jLogKind)
{
- return getOrdinal(JAVA_PACKAGE"/types/Mergeinfo$LogKind", jLogKind);
+ return getOrdinal(JAVAHL_CLASS("/types/Mergeinfo$LogKind"), jLogKind);
}
int EnumMapper::toLogLevel(jobject jLogLevel)
{
- return getOrdinal(JAVA_PACKAGE"/SVNClient$ClientLogLevel", jLogLevel);
+ return getOrdinal(JAVAHL_CLASS("/SVNClient$ClientLogLevel"), jLogLevel);
+}
+
+svn_node_kind_t EnumMapper::toNodeKind(jobject jNodeKind)
+{
+ return svn_node_kind_t(
+ getOrdinal(JAVAHL_CLASS("/types/NodeKind"), jNodeKind));
+}
+
+svn_checksum_kind_t EnumMapper::toChecksumKind(jobject jChecksumKind)
+{
+ return svn_checksum_kind_t(
+ getOrdinal(JAVAHL_CLASS("/types/Checksum$Kind"), jChecksumKind));
+}
+
+svn_tristate_t EnumMapper::toTristate(jobject jTristate)
+{
+ switch (getOrdinal(JAVAHL_CLASS("/types/Tristate"), jTristate))
+ {
+ case 1: return svn_tristate_false;
+ case 2: return svn_tristate_true;
+ default: return svn_tristate_unknown;
+ }
}
svn_depth_t EnumMapper::toDepth(jobject jdepth)
{
// The offset for depths is -2
- return static_cast<svn_depth_t>(getOrdinal(JAVA_PACKAGE"/types/Depth", jdepth) - 2);
+ return static_cast<svn_depth_t>(getOrdinal(JAVAHL_CLASS("/types/Depth"), jdepth) - 2);
}
+svn_mergeinfo_inheritance_t
+EnumMapper::toMergeinfoInheritance(jobject jInheritance)
+{
+ return static_cast<svn_mergeinfo_inheritance_t>(
+ getOrdinal(JAVAHL_CLASS("/types/Mergeinfo$Inheritance"), jInheritance));
+}
+
+
jobject EnumMapper::mapDepth(svn_depth_t depth)
{
// We're assuming a valid value for the C enum above
// The offset for depths is -2
- return mapEnum(JAVA_PACKAGE"/types/Depth", static_cast<int>(depth) + 2);
+ return mapEnum(JAVAHL_CLASS("/types/Depth"), static_cast<int>(depth) + 2);
}
jobject EnumMapper::mapOperation(svn_wc_operation_t operation)
{
// We're assuming a valid value for the C enum above
- return mapEnum(JAVA_PACKAGE"/ConflictDescriptor$Operation",
+ return mapEnum(JAVAHL_CLASS("/ConflictDescriptor$Operation"),
static_cast<int>(operation));
}
jobject EnumMapper::mapTristate(svn_tristate_t tristate)
{
// We're assuming a valid value for the C enum above
- return mapEnum(JAVA_PACKAGE"/types/Tristate",
+ return mapEnum(JAVAHL_CLASS("/types/Tristate"),
static_cast<int>(tristate - svn_tristate_false));
}
svn_wc_conflict_choice_t EnumMapper::toConflictChoice(jobject jchoice)
{
return static_cast<svn_wc_conflict_choice_t>
- (getOrdinal(JAVA_PACKAGE"/ConflictResult$Choice", jchoice));
+ (getOrdinal(JAVAHL_CLASS("/ConflictResult$Choice"), jchoice));
}
svn_opt_revision_kind EnumMapper::toRevisionKind(jobject jkind)
{
return static_cast<svn_opt_revision_kind>
- (getOrdinal(JAVA_PACKAGE"/types/Revision$Kind", jkind));
+ (getOrdinal(JAVAHL_CLASS("/types/Revision$Kind"), jkind));
}
jobject EnumMapper::mapSummarizeKind(svn_client_diff_summarize_kind_t sKind)
{
// We're assuming a valid value for the C enum above
- return mapEnum(JAVA_PACKAGE"/DiffSummary$DiffKind",
+ return mapEnum(JAVAHL_CLASS("/DiffSummary$DiffKind"),
static_cast<int>(sKind));
}
diff --git a/subversion/bindings/javahl/native/EnumMapper.h b/subversion/bindings/javahl/native/EnumMapper.h
index cb38bd9..3aa0423 100644
--- a/subversion/bindings/javahl/native/EnumMapper.h
+++ b/subversion/bindings/javahl/native/EnumMapper.h
@@ -48,9 +48,13 @@ 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);
+ static svn_tristate_t toTristate(jobject jTristate);
+ static svn_mergeinfo_inheritance_t
+ toMergeinfoInheritance(jobject jInheritance);
/* Converting from C enum's */
- static jint mapCommitMessageStateFlags(apr_byte_t flags);
static jobject mapChangePathAction(const char action);
static jobject mapNotifyState(svn_wc_notify_state_t state);
static jobject mapNotifyAction(svn_wc_notify_action_t action);
diff --git a/subversion/bindings/javahl/native/ExternalItem.cpp b/subversion/bindings/javahl/native/ExternalItem.cpp
new file mode 100644
index 0000000..acfa1fb
--- /dev/null
+++ b/subversion/bindings/javahl/native/ExternalItem.cpp
@@ -0,0 +1,176 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#include "jniwrapper/jni_stack.hpp"
+
+#include "ExternalItem.hpp"
+#include "Revision.h"
+
+namespace JavaHL {
+
+const char* const ExternalItem::m_class_name =
+ JAVAHL_CLASS("/types/ExternalItem");
+
+ExternalItem::ClassImpl::ClassImpl(::Java::Env env, jclass cls)
+ : ::Java::Object::ClassImpl(env, cls),
+ m_mid_ctor(
+ env.GetMethodID(cls, "<init>",
+ "(ZLjava/lang/String;Ljava/lang/String;"
+ JAVAHL_ARG("/types/Revision;")
+ JAVAHL_ARG("/types/Revision;")
+ ")V")),
+ m_fid_target_dir(env.GetFieldID(cls, "targetDir", "Ljava/lang/String;")),
+ m_fid_url(env.GetFieldID(cls, "url", "Ljava/lang/String;")),
+ m_fid_revision(env.GetFieldID(cls, "revision",
+ JAVAHL_ARG("/types/Revision;"))),
+ m_fid_peg_revision(env.GetFieldID(cls, "pegRevision",
+ JAVAHL_ARG("/types/Revision;")))
+{}
+
+ExternalItem::ClassImpl::~ClassImpl() {}
+
+namespace {
+inline jstring
+get_string_field(::Java::Env env, jobject jthis,
+ const ::Java::FieldID& fid)
+{
+ return jstring(env.GetObjectField(jthis, fid));
+}
+
+inline svn_opt_revision_t
+get_revision_field(::Java::Env env, jobject jthis,
+ const ::Java::FieldID& fid)
+{
+ const jobject rev = env.GetObjectField(jthis, fid);
+ return *Revision(rev).revision();
+}
+
+inline jobject
+make_external_item(::Java::Env env,
+ jclass cls, const ::Java::MethodID& mid_ctor,
+ const char* target_dir,
+ const char* url,
+ const svn_opt_revision_t* revision,
+ const svn_opt_revision_t* peg_revision)
+{
+ return env.NewObject(cls, mid_ctor,
+ JNI_FALSE,
+ env.NewStringUTF(target_dir),
+ env.NewStringUTF(url),
+ Revision::makeJRevision(*revision),
+ Revision::makeJRevision(*peg_revision));
+}
+} // anonymous namespace
+
+ExternalItem::ExternalItem(::Java::Env env, jobject jthis)
+ : Object(env, ::Java::ClassCache::get_external_item(env), jthis),
+ m_target_dir(env, get_string_field(env, jthis, impl().m_fid_target_dir)),
+ m_url(env, get_string_field(env, jthis, impl().m_fid_url)),
+ m_revision(get_revision_field(env, jthis, impl().m_fid_revision)),
+ m_peg_revision(get_revision_field(env, jthis, impl().m_fid_peg_revision))
+{}
+
+ExternalItem::ExternalItem(::Java::Env env,
+ const char* target_dir,
+ const char* url,
+ const svn_opt_revision_t* revision,
+ const svn_opt_revision_t* peg_revision)
+ : Object(env, ::Java::ClassCache::get_external_item(env)),
+ m_target_dir(env, target_dir),
+ m_url(env, url),
+ m_revision(*revision),
+ m_peg_revision(*peg_revision)
+{
+ set_this(make_external_item(env, get_class(), impl().m_mid_ctor,
+ target_dir, url, revision, peg_revision));
+}
+
+svn_wc_external_item2_t*
+ExternalItem::get_external_item(SVN::Pool& svnpool) const
+{
+ svn_wc_external_item2_t* item;
+ apr_pool_t* const pool = svnpool.getPool();
+ 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());
+ item->url = apr_pstrdup(
+ pool, Java::String::Contents(m_url).c_str());
+ item->revision = m_revision;
+ item->peg_revision = m_peg_revision;
+ return item;
+}
+
+} // namespace JavaHL
+
+
+// FIXME: Should be in another source file, but Revision.cpp is
+// old-style, so we'll put this implementation here for now.
+namespace {
+inline jobject get_static_revision(::Java::Env env, jclass cls,
+ const char* field_name)
+{
+ return env.GetStaticObjectField(
+ cls, env.GetStaticFieldID(cls, field_name,
+ JAVAHL_ARG("/types/Revision;")));
+}
+} // anonymous namespace
+
+jobject Revision::makeJRevision(const svn_opt_revision_t& rev)
+{
+ if (rev.kind == svn_opt_revision_number)
+ return Revision::makeJRevision(rev.value.number);
+
+ const ::Java::Env env;
+
+ if (rev.kind == svn_opt_revision_date)
+ {
+ const jclass cls = env.FindClass(
+ JAVAHL_CLASS("/types/Revision$DateSpec"));
+ return env.NewObject(cls, env.GetMethodID(cls, "<init>", "(J)V"),
+ jlong(rev.value.date / 1000));
+ }
+
+ const jclass cls = env.FindClass(JAVAHL_CLASS("/types/Revision"));
+ switch (rev.kind)
+ {
+ case svn_opt_revision_committed:
+ return get_static_revision(env, cls, "COMMITTED");
+
+ case svn_opt_revision_previous:
+ return get_static_revision(env, cls, "PREVIOUS");
+
+ case svn_opt_revision_base:
+ return get_static_revision(env, cls, "BASE");
+
+ case svn_opt_revision_working:
+ return get_static_revision(env, cls, "WORKING");
+
+ case svn_opt_revision_head:
+ return get_static_revision(env, cls, "HEAD");
+
+ case svn_opt_revision_unspecified:
+ default:
+ return get_static_revision(env, cls, "UNSPECIFIED");
+ }
+}
diff --git a/subversion/bindings/javahl/native/ExternalItem.hpp b/subversion/bindings/javahl/native/ExternalItem.hpp
new file mode 100644
index 0000000..75827ff
--- /dev/null
+++ b/subversion/bindings/javahl/native/ExternalItem.hpp
@@ -0,0 +1,139 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#ifndef SVN_JAVAHL_EXTERNAL_ITEM_HPP
+#define SVN_JAVAHL_EXTERNAL_ITEM_HPP
+
+#include <string>
+
+#include "svn_opt.h"
+#include "svn_wc.h"
+
+#include "Pool.h"
+
+#include "jniwrapper/jni_object.hpp"
+#include "jniwrapper/jni_string.hpp"
+
+namespace JavaHL {
+
+/**
+ * Object wrapper for @c org.apache.subversion.javahl.types.ExternalItem.
+ *
+ * @since New in 1.9.
+ */
+class ExternalItem : public ::Java::Object
+{
+public:
+ /**
+ * Constructs a wrapper around @a jthis.
+ * The constructor does not verify the class of the wrapped object.
+ */
+ explicit ExternalItem(::Java::Env env, jobject jthis);
+
+ /**
+ * Constructs a new @c ExternalItem object and wrapper.
+ */
+ explicit ExternalItem(::Java::Env env,
+ const char* target_dir,
+ const char* url,
+ const svn_opt_revision_t* revision,
+ const svn_opt_revision_t* peg_revision);
+
+ /**
+ * Returns the value of the wrapped object's @c targetDir member.
+ */
+ std::string target_dir() const
+ {
+ const ::Java::String::Contents contents(m_target_dir);
+ return std::string(contents.c_str());
+ }
+
+ /**
+ * Returns the value of the wrapped object's @c url member.
+ */
+ std::string url() const
+ {
+ const ::Java::String::Contents contents(m_url);
+ return std::string(contents.c_str());
+ }
+
+ /**
+ * Returns the value of the wrapped object's @c revision member.
+ */
+ const svn_opt_revision_t* revision() const
+ {
+ return &m_revision;
+ }
+
+ /**
+ * Returns the value of the wrapped object's @c pegRevision member.
+ */
+ const svn_opt_revision_t* peg_revision() const
+ {
+ return &m_peg_revision;
+ }
+
+ /**
+ * Returns an @c svn_wc_external_item2_t allocated from @a pool and
+ * filled in with this object's values.
+ */
+ svn_wc_external_item2_t* get_external_item(SVN::Pool& pool) const;
+
+private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ::Java::ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(::Java::Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const ::Java::MethodID m_mid_ctor;
+ const ::Java::FieldID m_fid_target_dir;
+ const ::Java::FieldID m_fid_url;
+ const ::Java::FieldID m_fid_revision;
+ const ::Java::FieldID m_fid_peg_revision;
+ };
+
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+
+ friend class ::Java::ClassCacheImpl;
+ static const char* const m_class_name;
+
+ ::Java::String m_target_dir;
+ ::Java::String m_url;
+ svn_opt_revision_t m_revision;
+ svn_opt_revision_t m_peg_revision;
+};
+
+} // namespace JavaHL
+
+#endif // SVN_JAVAHL_EXTERNAL_ITEM_HPP
diff --git a/subversion/bindings/javahl/native/GlobalConfig.h b/subversion/bindings/javahl/native/GlobalConfig.h
new file mode 100644
index 0000000..b7c561d
--- /dev/null
+++ b/subversion/bindings/javahl/native/GlobalConfig.h
@@ -0,0 +1,38 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file GlobalConfig.h
+ * @brief Interface of the class GlobalConfig
+ */
+
+#ifndef JAVAHL_GLOBAL_CONFIG_H
+#define JAVAHL_GLOBAL_CONFIG_H
+
+#include "JNIUtil.h"
+
+class GlobalConfig
+{
+ public:
+ static bool useNativeCredentialsStore();
+};
+
+#endif // JAVAHL_GLOBAL_CONFIG_H
diff --git a/subversion/bindings/javahl/native/ImportFilterCallback.cpp b/subversion/bindings/javahl/native/ImportFilterCallback.cpp
index 33d7e8b..3c4a4c5 100644
--- a/subversion/bindings/javahl/native/ImportFilterCallback.cpp
+++ b/subversion/bindings/javahl/native/ImportFilterCallback.cpp
@@ -83,13 +83,14 @@ ImportFilterCallback::doImportFilter(svn_boolean_t *filtered,
static jmethodID mid = 0;
if (mid == 0)
{
- jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/ImportFilterCallback");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/ImportFilterCallback"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN(SVN_NO_ERROR);
mid = env->GetMethodID(clazz, "filter",
"(Ljava/lang/String;"
- "L"JAVA_PACKAGE"/types/NodeKind;Z)Z");
+ JAVAHL_ARG("/types/NodeKind;")
+ "Z)Z");
if (JNIUtil::isJavaExceptionThrown() || mid == 0)
POP_AND_RETURN(SVN_NO_ERROR);
}
diff --git a/subversion/bindings/javahl/native/InfoCallback.cpp b/subversion/bindings/javahl/native/InfoCallback.cpp
index 8a9e375..ee8fb69 100644
--- a/subversion/bindings/javahl/native/InfoCallback.cpp
+++ b/subversion/bindings/javahl/native/InfoCallback.cpp
@@ -80,12 +80,12 @@ InfoCallback::singleInfo(const char *path,
static jmethodID mid = 0;
if (mid == 0)
{
- jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/InfoCallback");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/InfoCallback"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN(SVN_NO_ERROR);
mid = env->GetMethodID(clazz, "singleInfo",
- "(L"JAVA_PACKAGE"/types/Info;)V");
+ "(" JAVAHL_ARG("/types/Info;") ")V");
if (JNIUtil::isJavaExceptionThrown() || mid == 0)
POP_AND_RETURN(SVN_NO_ERROR);
}
@@ -95,8 +95,6 @@ InfoCallback::singleInfo(const char *path,
POP_AND_RETURN(SVN_NO_ERROR);
env->CallVoidMethod(m_callback, mid, jinfo2);
- // Return SVN_NO_ERROR here regardless of an exception or not.
- env->PopLocalFrame(NULL);
- return SVN_NO_ERROR;
+ POP_AND_RETURN_EXCEPTION_AS_SVNERROR();
}
diff --git a/subversion/bindings/javahl/native/InfoCallback.h b/subversion/bindings/javahl/native/InfoCallback.h
index b65f203..a1975d2 100644
--- a/subversion/bindings/javahl/native/InfoCallback.h
+++ b/subversion/bindings/javahl/native/InfoCallback.h
@@ -30,8 +30,6 @@
#include <jni.h>
#include "svn_client.h"
-struct info_entry;
-
/**
* This class holds a Java callback object, which will receive every line of
* the file for which the callback information is requested.
diff --git a/subversion/bindings/javahl/native/InputStream.cpp b/subversion/bindings/javahl/native/InputStream.cpp
index 1c73ba2..ac10f9e 100644
--- a/subversion/bindings/javahl/native/InputStream.cpp
+++ b/subversion/bindings/javahl/native/InputStream.cpp
@@ -54,7 +54,8 @@ svn_stream_t *InputStream::getStream(const SVN::Pool &pool)
// Create a stream with this as the baton and set the read and
// close functions.
svn_stream_t *ret = svn_stream_create(this, pool.getPool());
- svn_stream_set_read(ret, InputStream::read);
+ svn_stream_set_read2(ret, InputStream::read,
+ NULL /* only partial read support */);
svn_stream_set_close(ret, InputStream::close);
return ret;
}
@@ -68,6 +69,9 @@ svn_stream_t *InputStream::getStream(const SVN::Pool &pool)
*/
svn_error_t *InputStream::read(void *baton, char *buffer, apr_size_t *len)
{
+ if (0 == *len)
+ return SVN_NO_ERROR;
+
JNIEnv *env = JNIUtil::getEnv();
// An object of our class is passed in as the baton.
InputStream *that = static_cast<InputStream *>(baton);
diff --git a/subversion/bindings/javahl/native/Iterator.cpp b/subversion/bindings/javahl/native/Iterator.cpp
new file mode 100644
index 0000000..bd8aee0
--- /dev/null
+++ b/subversion/bindings/javahl/native/Iterator.cpp
@@ -0,0 +1,115 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file Iterator.cpp
+ * @brief Implementation of the class Iterator
+ */
+
+#include "Iterator.h"
+
+namespace {
+jobject init_iterator(jobject jiterable, bool persistent)
+{
+ // A null iterable is allowed, we'll leave the iterator null and
+ // nothing will happen in hasNext() and next().
+ if (!jiterable)
+ return NULL;
+
+ JNIEnv* env = JNIUtil::getEnv();
+
+ static jmethodID iterator_mid = 0;
+ if (0 == iterator_mid)
+ {
+ jclass cls = env->FindClass("java/lang/Iterable");
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ iterator_mid = env->GetMethodID(cls, "iterator",
+ "()Ljava/util/Iterator;");
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ }
+
+ jobject jiterator = env->CallObjectMethod(jiterable, iterator_mid);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ return (persistent ? env->NewGlobalRef(jiterator) : jiterator);
+}
+} // anonymous namespace
+
+
+Iterator::Iterator(jobject jiterable)
+ : m_persistent(false),
+ m_jiterator(init_iterator(jiterable, false))
+{}
+
+Iterator::Iterator(jobject jiterable, bool)
+ : m_persistent(true),
+ m_jiterator(init_iterator(jiterable, true))
+{}
+
+Iterator::~Iterator()
+{
+ if (m_persistent && m_jiterator)
+ JNIUtil::getEnv()->DeleteGlobalRef(m_jiterator);
+}
+
+bool Iterator::hasNext() const
+{
+ if (!m_jiterator)
+ return false;
+
+ JNIEnv* env = JNIUtil::getEnv();
+
+ static jmethodID hasNext_mid = 0;
+ if (0 == hasNext_mid)
+ {
+ jclass cls = env->FindClass("java/util/Iterator");
+ if (JNIUtil::isExceptionThrown())
+ return false;
+ hasNext_mid = env->GetMethodID(cls, "hasNext", "()Z");
+ if (JNIUtil::isExceptionThrown())
+ return false;
+ }
+
+ return bool(env->CallBooleanMethod(m_jiterator, hasNext_mid));
+}
+
+jobject Iterator::next() const
+{
+ if (!m_jiterator)
+ return NULL;
+
+ JNIEnv* env = JNIUtil::getEnv();
+
+ static jmethodID next_mid = 0;
+ if (0 == next_mid)
+ {
+ jclass cls = env->FindClass("java/util/Iterator");
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ next_mid = env->GetMethodID(cls, "next", "()Ljava/lang/Object;");
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ }
+
+ return env->CallObjectMethod(m_jiterator, next_mid);
+}
diff --git a/subversion/bindings/javahl/native/Iterator.h b/subversion/bindings/javahl/native/Iterator.h
new file mode 100644
index 0000000..c116ab3
--- /dev/null
+++ b/subversion/bindings/javahl/native/Iterator.h
@@ -0,0 +1,64 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file Iterator.cpp
+ * @brief Interface of the class Iterator
+ */
+
+#ifndef JAVAHL_ITERATOR_H
+#define JAVAHL_ITERATOR_H
+
+#include "JNIUtil.h"
+
+/**
+ * Encapsulates an immutable java.lang.Iterator implementation.
+ */
+class Iterator
+{
+public:
+ Iterator(jobject jiterable);
+ ~Iterator();
+ bool hasNext() const;
+ jobject next() const;
+
+protected:
+ Iterator(jobject jiterable, bool);
+
+private:
+ bool m_persistent;
+ jobject m_jiterator;
+};
+
+
+/**
+ * Like Iterator, but the implementation will hold a global reference
+ * to the internal iterator object to protect it across JNI calls.
+ */
+class PersistentIterator : protected Iterator
+{
+public:
+ PersistentIterator(jobject jiterable) : Iterator(jiterable, true) {}
+ bool hasNext() const { return Iterator::hasNext(); }
+ jobject next() const { return Iterator::next(); }
+};
+
+#endif // JAVAHL_ITERATOR_H
diff --git a/subversion/bindings/javahl/native/JNIByteArray.cpp b/subversion/bindings/javahl/native/JNIByteArray.cpp
index 34ce96c..cedb1bb 100644
--- a/subversion/bindings/javahl/native/JNIByteArray.cpp
+++ b/subversion/bindings/javahl/native/JNIByteArray.cpp
@@ -32,30 +32,24 @@
* @param flag that the underlying byte array reference should be deleted at
* destruction
*/
-JNIByteArray::JNIByteArray(jbyteArray jba, bool deleteByteArray)
-{
- m_array = jba;
- m_deleteByteArray = deleteByteArray;
- if (jba != NULL)
- {
- // Get the bytes.
- JNIEnv *env = JNIUtil::getEnv();
- m_data = env->GetByteArrayElements(jba, NULL);
- }
- else
- {
- m_data = NULL;
- }
-}
+JNIByteArray::JNIByteArray(jbyteArray jba,
+ bool deleteByteArray,
+ bool abortOnRelease)
+ : m_array(jba),
+ m_data(!jba ? NULL
+ : JNIUtil::getEnv()->GetByteArrayElements(jba, NULL)),
+ m_deleteByteArray(deleteByteArray),
+ m_abortOnRelease(abortOnRelease)
+{}
JNIByteArray::~JNIByteArray()
{
if (m_array != NULL)
{
// Release the bytes
- JNIUtil::getEnv()->ReleaseByteArrayElements(m_array,
- m_data,
- JNI_ABORT);
+ JNIUtil::getEnv()->ReleaseByteArrayElements(
+ m_array, m_data,
+ (m_abortOnRelease ? JNI_ABORT: JNI_COMMIT));
if (m_deleteByteArray)
// And if needed the byte array.
JNIUtil::getEnv()->DeleteLocalRef(m_array);
diff --git a/subversion/bindings/javahl/native/JNIByteArray.h b/subversion/bindings/javahl/native/JNIByteArray.h
index b43673b..e988bda 100644
--- a/subversion/bindings/javahl/native/JNIByteArray.h
+++ b/subversion/bindings/javahl/native/JNIByteArray.h
@@ -51,11 +51,18 @@ class JNIByteArray
* at destruction.
*/
bool m_deleteByteArray;
+
+ /**
+ * False if changes to the array should be committed to the Java VM.
+ */
+ bool m_abortOnRelease;
public:
bool isNull() const;
const signed char *getBytes() const;
int getLength();
- JNIByteArray(jbyteArray jba, bool deleteByteArray = false);
+ JNIByteArray(jbyteArray jba,
+ bool deleteByteArray = false,
+ bool abortOnRelease = true);
~JNIByteArray();
};
diff --git a/subversion/bindings/javahl/native/JNICriticalSection.h b/subversion/bindings/javahl/native/JNICriticalSection.h
index 685b049..d628c7e 100644
--- a/subversion/bindings/javahl/native/JNICriticalSection.h
+++ b/subversion/bindings/javahl/native/JNICriticalSection.h
@@ -32,7 +32,7 @@ class JNIMutex;
/**
* This class holds a mutex which will be locked during the constructor and
* released during the destructor. If the object is created on the stack, this
- * garanties that the mutex will be released all the time if the block is left.
+ * guaranties that the mutex will be released all the time if the block is left.
* Only one thread can enter all the critrical sections secured by the same
* mutex.
*/
diff --git a/subversion/bindings/javahl/native/JNIStackElement.cpp b/subversion/bindings/javahl/native/JNIStackElement.cpp
index 2ca6c6e..df9163d 100644
--- a/subversion/bindings/javahl/native/JNIStackElement.cpp
+++ b/subversion/bindings/javahl/native/JNIStackElement.cpp
@@ -27,7 +27,6 @@
#include "JNIStackElement.h"
#include "JNIUtil.h"
#include "JNIStringHolder.h"
-#include "JNIThreadData.h"
#include <apr_strings.h>
/**
@@ -74,7 +73,7 @@ JNIStackElement::JNIStackElement(JNIEnv *env, const char *clazz,
// Copy the result to a buffer.
JNIStringHolder name(reinterpret_cast<jstring>(oStr));
- strncat(m_objectID, name, JNIUtil::formatBufferSize -1);
+ strncat(m_objectID, name, sizeof(m_objectID) - 1);
env->DeleteLocalRef(oStr);
}
@@ -86,8 +85,8 @@ JNIStackElement::JNIStackElement(JNIEnv *env, const char *clazz,
m_method = method;
// Generate the log message.
- char *buffer = JNIUtil::getFormatBuffer();
- apr_snprintf(buffer, JNIUtil::formatBufferSize,
+ char buffer[2048];
+ apr_snprintf(buffer, sizeof(buffer),
"entry class %s method %s object %s", m_clazz, m_method,
m_objectID);
JNIUtil::logMessage(buffer);
@@ -110,11 +109,10 @@ JNIStackElement::~JNIStackElement()
if (m_clazz != NULL)
{
// Generate a log message.
- char *buffer = JNIUtil::getFormatBuffer();
- apr_snprintf(buffer, JNIUtil::formatBufferSize,
+ char buffer[2048];
+ apr_snprintf(buffer, sizeof(buffer),
"exit class %s method %s object %s", m_clazz,
m_method, m_objectID);
JNIUtil::logMessage(buffer);
}
- JNIThreadData::popThreadData();
}
diff --git a/subversion/bindings/javahl/native/JNIStackElement.h b/subversion/bindings/javahl/native/JNIStackElement.h
index 99dab62..61708c3 100644
--- a/subversion/bindings/javahl/native/JNIStackElement.h
+++ b/subversion/bindings/javahl/native/JNIStackElement.h
@@ -73,7 +73,7 @@ class JNIStackElement
* A buffer for the result for jthis.toString to identify the
* object.
*/
- char m_objectID[JNIUtil::formatBufferSize];
+ char m_objectID[2048];
};
#endif // JNISTACKELEMENT_H
diff --git a/subversion/bindings/javahl/native/JNIStringHolder.h b/subversion/bindings/javahl/native/JNIStringHolder.h
index 7d36bed..c1bbf72 100644
--- a/subversion/bindings/javahl/native/JNIStringHolder.h
+++ b/subversion/bindings/javahl/native/JNIStringHolder.h
@@ -36,6 +36,7 @@ class JNIStringHolder
JNIStringHolder(jstring jtext);
~JNIStringHolder();
operator const char *() { return m_str; }
+ const char* c_str() const { return m_str; }
const char *pstrdup(apr_pool_t *pool);
protected:
diff --git a/subversion/bindings/javahl/native/JNIThreadData.cpp b/subversion/bindings/javahl/native/JNIThreadData.cpp
deleted file mode 100644
index e3ce334..0000000
--- a/subversion/bindings/javahl/native/JNIThreadData.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-/**
- * @copyright
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- * @endcopyright
- *
- * @file JNIThreadData.cpp
- * @brief Implementation of the class JNIThreadData
- */
-
-#include "JNIThreadData.h"
-#include <apr_strings.h>
-#include <apr_tables.h>
-#include <apr_general.h>
-#include <apr_lib.h>
-#include <apr_thread_proc.h>
-#include "JNIUtil.h"
-
-apr_threadkey_t *JNIThreadData::g_key;
-
-/**
- * Create and initialize a new object.
- */
-JNIThreadData::JNIThreadData()
-{
- m_env = NULL;
- m_exceptionThrown = false;
- m_previous = NULL;
-}
-
-JNIThreadData::~JNIThreadData()
-{
-}
-
-/**
- * Initialize the thread local storage.
- * @return success or failure
- */
-bool JNIThreadData::initThreadData()
-{
- // If already initialized -> nothing to do.
- if (g_key != NULL)
- return false;
-
- // Request a key for the thread local storage from the global pool
- // and register a callback function called when the thread is
- // deleted.
- apr_status_t apr_err = apr_threadkey_private_create(&g_key,
- del,
- JNIUtil::getPool());
- if (apr_err)
- {
- JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_create");
- return false;
- }
-
- return true;
-}
-
-/**
- * Get the thread local storage for this thread.
- * @return thread local storage
- */
-JNIThreadData *JNIThreadData::getThreadData()
-{
- // We should never be called before initThreadData
- if (g_key == NULL)
- return NULL;
-
- // Retrieve the thread local storage from APR.
- JNIThreadData *data = NULL;
- apr_status_t apr_err = apr_threadkey_private_get
- (reinterpret_cast<void**>(&data), g_key);
- if (apr_err)
- {
- JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_get");
- return NULL;
- }
-
- // Not already allocated.
- if (data == NULL)
- {
- // Allocate and store to APR.
- data = new JNIThreadData;
- apr_err = apr_threadkey_private_set (data, g_key);
- if (apr_err)
- {
- JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_set");
- return NULL;
- }
- }
- return data;
-}
-
-/**
- * Allocate a new ThreadData for the current call from Java and push
- * it on the stack
- */
-void JNIThreadData::pushNewThreadData()
-{
- JNIThreadData *data = NULL;
- apr_status_t apr_err = apr_threadkey_private_get
- (reinterpret_cast<void**>(&data), g_key);
- if (apr_err)
- {
- JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_get");
- return;
- }
- JNIThreadData *newData = new JNIThreadData();
- newData->m_previous =data;
- apr_err = apr_threadkey_private_set(newData, g_key);
- if (apr_err)
- {
- JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_set");
- return;
- }
-}
-
-/**
- * Pop the current ThreadData from the stack, because the call
- * completed.
- */
-void JNIThreadData::popThreadData()
-{
- JNIThreadData *data = NULL;
- apr_status_t apr_err = apr_threadkey_private_get
- (reinterpret_cast<void**>(&data), g_key);
- if (apr_err)
- {
- JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_get");
- return;
- }
- if (data == NULL)
- return;
-
- JNIThreadData *oldData = data->m_previous;
- delete data;
- apr_err = apr_threadkey_private_set(oldData, g_key);
- if (apr_err)
- {
- JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_set");
- return;
- }
-}
-
-/**
- * Callback called by APR when the thread dies. Deletes the thread
- * local storage.
- */
-void JNIThreadData::del(void *p)
-{
- delete reinterpret_cast<JNIThreadData*>(p);
-}
diff --git a/subversion/bindings/javahl/native/JNIUtil.cpp b/subversion/bindings/javahl/native/JNIUtil.cpp
index e52e975..2adcf2f 100644
--- a/subversion/bindings/javahl/native/JNIUtil.cpp
+++ b/subversion/bindings/javahl/native/JNIUtil.cpp
@@ -24,6 +24,13 @@
* @brief Implementation of the class JNIUtil
*/
+/* Include apr.h first, or INT64_C won't be defined properly on some C99
+ compilers, when other headers include <stdint.h> before defining some
+ macros.
+
+ See apr.h for the ugly details */
+#include <apr.h>
+
#include "JNIUtil.h"
#include "Array.h"
@@ -35,8 +42,11 @@
#include <apr_tables.h>
#include <apr_general.h>
#include <apr_lib.h>
+#include <apr_file_info.h>
+#include <apr_time.h>
#include "svn_pools.h"
+#include "svn_error.h"
#include "svn_fs.h"
#include "svn_ra.h"
#include "svn_utf.h"
@@ -44,37 +54,43 @@
#include "svn_dso.h"
#include "svn_path.h"
#include "svn_cache_config.h"
-#include <apr_file_info.h>
+#include "private/svn_atomic.h"
+#include "private/svn_utf_private.h"
#include "svn_private_config.h"
-#ifdef WIN32
-/* FIXME: We're using an internal APR header here, which means we
- have to build Subversion with APR sources. This being Win32-only,
- that should be fine for now, but a better solution must be found in
- combination with issue #850. */
-extern "C" {
-#include <arch/win32/apr_arch_utf8.h>
-};
-#endif
#include "SVNBase.h"
#include "JNIMutex.h"
#include "JNICriticalSection.h"
-#include "JNIThreadData.h"
#include "JNIStringHolder.h"
#include "Pool.h"
+
+#include "jniwrapper/jni_env.hpp"
+
// Static members of JNIUtil are allocated here.
apr_pool_t *JNIUtil::g_pool = NULL;
std::list<SVNBase*> JNIUtil::g_finalizedObjects;
JNIMutex *JNIUtil::g_finalizedObjectsMutex = NULL;
JNIMutex *JNIUtil::g_logMutex = NULL;
+JNIMutex *JNIUtil::g_configMutex = NULL;
bool JNIUtil::g_initException;
-bool JNIUtil::g_inInit;
-JNIEnv *JNIUtil::g_initEnv;
-char JNIUtil::g_initFormatBuffer[formatBufferSize];
int JNIUtil::g_logLevel = JNIUtil::noLog;
std::ofstream JNIUtil::g_logStream;
+/* The error code we will use to signal a Java exception */
+static const apr_status_t
+SVN_ERR_JAVAHL_WRAPPED = SVN_ERR_MALFUNC_CATEGORY_START
+ + SVN_ERR_CATEGORY_SIZE - 10;
+
+/**
+ * Return the JNI environment to use
+ * @return the JNI environment
+ */
+JNIEnv *JNIUtil::getEnv()
+{
+ return Java::Env().get();
+}
+
/**
* Initialize the environment for all requests.
* @param env the JNI environment for this request
@@ -84,9 +100,6 @@ bool JNIUtil::JNIInit(JNIEnv *env)
// Clear all standing exceptions.
env->ExceptionClear();
- // Remember the env parameter for the remainder of the request.
- setEnv(env);
-
// Lock the list of finalized objects.
JNICriticalSection cs(*g_finalizedObjectsMutex) ;
if (isExceptionThrown())
@@ -104,49 +117,48 @@ bool JNIUtil::JNIInit(JNIEnv *env)
return true;
}
+/* Forwarder for calling JNIGlobalInit from JNI_OnLoad(). */
+bool initialize_jni_util(JNIEnv *env)
+{
+ return JNIUtil::JNIGlobalInit(env);
+}
+
+namespace {
+
+volatile svn_atomic_t *gentle_crash_write_loc = NULL;
+
+svn_error_t *
+gently_crash_the_jvm(svn_boolean_t can_return,
+ const char *file, int line, const char *expr)
+{
+ if (!can_return)
+ {
+ // Try not to abort; aborting prevents the JVM from creating
+ // a crash log, which is oh so useful for debugging.
+ // We can't just raise a SEGV signal, either, because it will
+ // be not be caught in the context that we're interested in
+ // getting the stack trace from.
+
+ // Try reading from and writing to the zero page
+ const svn_atomic_t zeropage = svn_atomic_read(gentle_crash_write_loc);
+ svn_atomic_set(gentle_crash_write_loc, zeropage);
+ }
+
+ // Forward to the standard malfunction handler, which does call
+ // abort when !can_return; this will only happen if the write to the
+ // zero page did not cause a SEGV.
+ return svn_error_raise_on_malfunction(can_return, file, line, expr);
+}
+} // Anonymous namespace
+
/**
* Initialize the environment for all requests.
+ * This method must be called in a single-threaded context.
* @param env the JNI environment for this request
*/
bool JNIUtil::JNIGlobalInit(JNIEnv *env)
{
- // This method has to be run only once during the run a program.
- static bool run = false;
svn_error_t *err;
- if (run) // already run
- return true;
-
- run = true;
-
- // Do not run this part more than one time. This leaves a small
- // time window when two threads create their first SVNClient and
- // SVNAdmin at the same time, but I do not see a better option
- // without APR already initialized
- if (g_inInit)
- return false;
-
- g_inInit = true;
- g_initEnv = env;
-
- apr_status_t status;
-
-
-
- /* Initialize the APR subsystem, and register an atexit() function
- * to Uninitialize that subsystem at program exit. */
- status = apr_initialize();
- if (status)
- {
- if (stderr)
- {
- char buf[1024];
- apr_strerror(status, buf, sizeof(buf) - 1);
- fprintf(stderr,
- "%s: error: cannot initialize APR: %s\n",
- "svnjavahl", buf);
- }
- return FALSE;
- }
/* This has to happen before any pools are created. */
if ((err = svn_dso_initialize2()))
@@ -158,16 +170,8 @@ bool JNIUtil::JNIGlobalInit(JNIEnv *env)
return FALSE;
}
- if (0 > atexit(apr_terminate))
- {
- if (stderr)
- fprintf(stderr,
- "%s: error: atexit registration failed\n",
- "svnjavahl");
- return FALSE;
- }
-
- /* Create our top-level pool. */
+ /* Create our top-level pool.
+ N.B.: APR was initialized by JNI_OnLoad. */
g_pool = svn_pool_create(NULL);
apr_allocator_t* allocator = apr_pool_allocator_get(g_pool);
@@ -180,15 +184,25 @@ bool JNIUtil::JNIGlobalInit(JNIEnv *env)
}
svn_utf_initialize2(FALSE, g_pool); /* Optimize character conversions */
- svn_fs_initialize(g_pool); /* Avoid some theoretical issues */
- svn_ra_initialize(g_pool);
- /* We shouldn't fill the JVMs memory with FS cache data unless explictly
- requested. */
+ // Initialize the libraries we use
+ err = svn_fs_initialize(g_pool);
+ if (!err)
+ err = svn_ra_initialize(g_pool);
+ if (err)
+ {
+ if (stderr && err->message)
+ fprintf(stderr, "%s", err->message);
+
+ svn_error_clear(err);
+ return FALSE;
+ }
+
+ /* We shouldn't fill the JVMs memory with FS cache data unless
+ explicitly requested. And we don't either, because the caches get
+ allocated outside the JVM heap. Duh. */
{
svn_cache_config_t settings = *svn_cache_config_get();
- settings.cache_size = 0;
- settings.file_handle_count = 0;
settings.single_threaded = FALSE;
svn_cache_config_set(&settings);
}
@@ -197,32 +211,24 @@ bool JNIUtil::JNIGlobalInit(JNIEnv *env)
#ifdef WIN32
{
WCHAR ucs2_path[MAX_PATH];
- char *utf8_path;
+ const char *utf8_path;
const char *internal_path;
- apr_pool_t *pool;
- apr_status_t apr_err;
- apr_size_t inwords, outbytes;
- unsigned int outlength;
+ svn_error_t *err;
+ apr_pool_t *pool = svn_pool_create(g_pool);
- pool = svn_pool_create(g_pool);
/* get dll name - our locale info will be in '../share/locale' */
- inwords = sizeof(ucs2_path) / sizeof(ucs2_path[0]);
HINSTANCE moduleHandle = GetModuleHandle("libsvnjavahl-1");
- GetModuleFileNameW(moduleHandle, ucs2_path, inwords);
- inwords = lstrlenW(ucs2_path);
- outbytes = outlength = 3 * (inwords + 1);
- utf8_path = reinterpret_cast<char *>(apr_palloc(pool, outlength));
- apr_err = apr_conv_ucs2_to_utf8((const apr_wchar_t *) ucs2_path,
- &inwords, utf8_path, &outbytes);
- if (!apr_err && (inwords > 0 || outbytes == 0))
- apr_err = APR_INCOMPLETE;
- if (apr_err)
+ GetModuleFileNameW(moduleHandle, ucs2_path,
+ sizeof(ucs2_path) / sizeof(ucs2_path[0]));
+ err = svn_utf__win32_utf16_to_utf8(&utf8_path, ucs2_path, NULL, pool);
+ if (err)
{
if (stderr)
- fprintf(stderr, "Can't convert module path to UTF-8");
- return FALSE;
+ svn_handle_error2(err, stderr, false, "svn: ");
+ svn_error_clear(err);
+ return false;
}
- utf8_path[outlength - outbytes] = '\0';
+
internal_path = svn_dirent_internal_style(utf8_path, pool);
/* get base path name */
internal_path = svn_dirent_dirname(internal_path, pool);
@@ -268,16 +274,14 @@ bool JNIUtil::JNIGlobalInit(JNIEnv *env)
if (isExceptionThrown())
return false;
- // initialized the thread local storage
- if (!JNIThreadData::initThreadData())
- return false;
-
- setEnv(env);
+ g_configMutex = new JNIMutex(g_pool);
if (isExceptionThrown())
return false;
- g_initEnv = NULL;
- g_inInit = false;
+ // Set a malfunction handler that tries not to call abort, because
+ // that would prevent the JVM from creating a crash and stack log file.
+ svn_error_set_malfunction_handler(gently_crash_the_jvm);
+
return true;
}
@@ -304,18 +308,6 @@ void JNIUtil::raiseThrowable(const char *name, const char *message)
return;
env->ThrowNew(clazz, message);
- setExceptionThrown();
- env->DeleteLocalRef(clazz);
-}
-
-jstring JNIUtil::makeSVNErrorMessage(svn_error_t *err)
-{
- if (err == NULL)
- return NULL;
- std::string buffer;
- assembleErrorMessage(err, 0, APR_SUCCESS, buffer);
- jstring jmessage = makeJString(buffer.c_str());
- return jmessage;
}
void
@@ -418,10 +410,174 @@ JNIUtil::putErrorsInTrace(svn_error_t *err,
env->DeleteLocalRef(jfileName);
}
-void JNIUtil::wrappedHandleSVNError(svn_error_t *err)
+namespace {
+struct MessageStackItem
{
- std::string msg;
- assembleErrorMessage(svn_error_purge_tracing(err), 0, APR_SUCCESS, msg);
+ apr_status_t m_code;
+ std::string m_message;
+ bool m_generic;
+
+ MessageStackItem(apr_status_t code, const char* message,
+ bool generic = false)
+ : m_code(code),
+ m_message(message),
+ m_generic(generic)
+ {}
+};
+typedef std::vector<MessageStackItem> ErrorMessageStack;
+
+/*
+ * Build the error message from the svn error into buffer. This
+ * method iterates through all the chained errors
+ *
+ * @param err the subversion error
+ * @param buffer the buffer where the formated error message will
+ * be stored
+ * @return An array of error codes and messages
+ */
+ErrorMessageStack assemble_error_message(
+ svn_error_t *err, std::string &result)
+{
+ // buffer for a single error message
+ char errbuf[1024];
+ apr_status_t parent_apr_err = 0;
+ ErrorMessageStack message_stack;
+
+ /* Pretty-print the error */
+ /* Note: we can also log errors here someday. */
+
+ for (int depth = 0; err;
+ ++depth, parent_apr_err = err->apr_err, err = err->child)
+ {
+ /* When we're recursing, don't repeat the top-level message if its
+ * the same as before. */
+ if ((depth == 0 || err->apr_err != parent_apr_err)
+ && err->apr_err != SVN_ERR_JAVAHL_WRAPPED)
+ {
+ 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))
+ 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. */
+ apr_strerror(err->apr_err, errbuf, sizeof(errbuf));
+ 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);
+ message = svn_utf_cstring_from_utf8_fuzzy(errbuf, err->pool);
+ }
+ }
+
+ message_stack.push_back(
+ MessageStackItem(err->apr_err, message, true));
+ }
+ if (err->message)
+ {
+ message_stack.push_back(
+ MessageStackItem(err->apr_err, err->message));
+ }
+ }
+
+ for (ErrorMessageStack::const_iterator it = message_stack.begin();
+ it != message_stack.end(); ++it)
+ {
+ if (!it->m_generic)
+ result += "svn: ";
+ result += it->m_message;
+ result += '\n';
+ }
+ return message_stack;
+}
+
+jobject construct_Jmessage_stack(const ErrorMessageStack& 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(JAVAHL_CLASS("/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 (ErrorMessageStack::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
+
+std::string JNIUtil::makeSVNErrorMessage(svn_error_t *err,
+ jstring *jerror_message,
+ jobject *jmessage_stack)
+{
+ if (jerror_message)
+ *jerror_message = NULL;
+ if (jmessage_stack)
+ *jmessage_stack = NULL;
+
+ std::string buffer;
+ err = svn_error_purge_tracing(err);
+ if (err == NULL || err->apr_err == 0
+ || !(jerror_message || jmessage_stack))
+ return buffer;
+
+ ErrorMessageStack message_stack = assemble_error_message(err, buffer);
+ if (jerror_message)
+ *jerror_message = makeJString(buffer.c_str());
+ if (jmessage_stack)
+ *jmessage_stack = construct_Jmessage_stack(message_stack);
+ return buffer;
+}
+
+jthrowable JNIUtil::wrappedCreateClientException(svn_error_t *err, jthrowable jcause)
+{
+ jstring jmessage;
+ jobject jstack;
+ std::string msg = makeSVNErrorMessage(err, &jmessage, &jstack);
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+
const char *source = NULL;
#ifdef SVN_DEBUG
#ifndef SVN_ERR__TRACING
@@ -436,6 +592,9 @@ void JNIUtil::wrappedHandleSVNError(svn_error_t *err)
#endif
#endif
+ if (!jcause)
+ jcause = JNIUtil::unwrapJavaException(err);
+
// Much of the following is stolen from throwNativeException(). As much as
// we'd like to call that function, we need to do some manual stack
// unrolling, so it isn't feasible.
@@ -445,11 +604,11 @@ void JNIUtil::wrappedHandleSVNError(svn_error_t *err)
// Create a local frame for our references
env->PushLocalFrame(LOCAL_FRAME_SIZE);
if (JNIUtil::isJavaExceptionThrown())
- return;
+ return NULL;
- jclass clazz = env->FindClass(JAVA_PACKAGE "/ClientException");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/ClientException"));
if (isJavaExceptionThrown())
- POP_AND_RETURN_NOTHING();
+ POP_AND_RETURN_NULL;
if (getLogLevel() >= exceptionLog)
{
@@ -463,23 +622,24 @@ void JNIUtil::wrappedHandleSVNError(svn_error_t *err)
g_logStream << std::endl;
}
if (isJavaExceptionThrown())
- POP_AND_RETURN_NOTHING();
+ POP_AND_RETURN_NULL;
- jstring jmessage = makeJString(msg.c_str());
- if (isJavaExceptionThrown())
- POP_AND_RETURN_NOTHING();
jstring jsource = makeJString(source);
if (isJavaExceptionThrown())
- POP_AND_RETURN_NOTHING();
+ POP_AND_RETURN_NULL;
jmethodID mid = env->GetMethodID(clazz, "<init>",
- "(Ljava/lang/String;Ljava/lang/String;I)V");
+ "(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,
- static_cast<jint>(err->apr_err));
+ POP_AND_RETURN_NULL;
+ jobject nativeException = env->NewObject(clazz, mid, jmessage, jcause,
+ jsource, jint(err->apr_err),
+ jstack);
if (isJavaExceptionThrown())
- POP_AND_RETURN_NOTHING();
+ POP_AND_RETURN_NULL;
#ifdef SVN_ERR__TRACING
// Add all the C error stack trace information to the Java Exception
@@ -491,7 +651,7 @@ void JNIUtil::wrappedHandleSVNError(svn_error_t *err)
mid_gst = env->GetMethodID(clazz, "getStackTrace",
"()[Ljava/lang/StackTraceElement;");
if (isJavaExceptionThrown())
- POP_AND_RETURN_NOTHING();
+ POP_AND_RETURN_NULL;
}
Array stackTraceArray((jobjectArray) env->CallObjectMethod(nativeException,
mid_gst));
@@ -510,18 +670,18 @@ void JNIUtil::wrappedHandleSVNError(svn_error_t *err)
jclass stClazz = env->FindClass("java/lang/StackTraceElement");
if (isJavaExceptionThrown())
- POP_AND_RETURN_NOTHING();
+ POP_AND_RETURN_NULL;
const jsize stSize = static_cast<jsize>(newStackTrace.size());
- if (stSize != newStackTrace.size())
+ if (stSize < 0 || stSize != newStackTrace.size())
{
env->ThrowNew(env->FindClass("java.lang.ArithmeticException"),
"Overflow converting C size_t to JNI jsize");
- POP_AND_RETURN_NOTHING();
+ POP_AND_RETURN_NULL;
}
jobjectArray jStackTrace = env->NewObjectArray(stSize, stClazz, NULL);
if (isJavaExceptionThrown())
- POP_AND_RETURN_NOTHING();
+ POP_AND_RETURN_NULL;
int i = 0;
for (std::vector<jobject>::const_iterator it = newStackTrace.begin();
@@ -538,25 +698,34 @@ void JNIUtil::wrappedHandleSVNError(svn_error_t *err)
mid_sst = env->GetMethodID(clazz, "setStackTrace",
"([Ljava/lang/StackTraceElement;)V");
if (isJavaExceptionThrown())
- POP_AND_RETURN_NOTHING();
+ POP_AND_RETURN_NULL;
}
env->CallVoidMethod(nativeException, mid_sst, jStackTrace);
if (isJavaExceptionThrown())
- POP_AND_RETURN_NOTHING();
+ POP_AND_RETURN_NULL;
#endif
- env->Throw(static_cast<jthrowable>(env->PopLocalFrame(nativeException)));
+ return static_cast<jthrowable>(env->PopLocalFrame(nativeException));
}
-void JNIUtil::handleSVNError(svn_error_t *err)
+jthrowable JNIUtil::createClientException(svn_error_t *err, jthrowable jcause)
{
+ jthrowable jexc = NULL;
try {
- wrappedHandleSVNError(err);
+ jexc = wrappedCreateClientException(err, jcause);
} catch (...) {
svn_error_clear(err);
throw;
}
svn_error_clear(err);
+ return jexc;
+}
+
+void JNIUtil::handleSVNError(svn_error_t *err, jthrowable jcause)
+{
+ jthrowable jexc = createClientException(err, jcause);
+ if (jexc)
+ getEnv()->Throw(jexc);
}
void JNIUtil::putFinalizedClient(SVNBase *object)
@@ -578,120 +747,82 @@ void JNIUtil::enqueueForDeletion(SVNBase *object)
*/
void JNIUtil::handleAPRError(int error, const char *op)
{
- char *buffer = getFormatBuffer();
- if (buffer == NULL)
- return;
+ char buffer[2048];
- apr_snprintf(buffer, formatBufferSize,
+ apr_snprintf(buffer, sizeof(buffer),
_("an error occurred in function %s with return value %d"),
op, error);
throwError(buffer);
}
-/**
- * Return if an exception has been detected.
- * @return a exception has been detected
- */
-bool JNIUtil::isExceptionThrown()
+namespace {
+const char* known_exception_to_cstring(apr_pool_t* pool)
{
- // During init -> look in the global member.
- if (g_inInit)
- return g_initException;
-
- // Look in the thread local storage.
- JNIThreadData *data = JNIThreadData::getThreadData();
- return data == NULL || data->m_exceptionThrown;
-}
+ JNIEnv *env = JNIUtil::getEnv();
+ jthrowable t = env->ExceptionOccurred();
+ jclass cls = env->GetObjectClass(t);
-/**
- * Store the JNI environment for this request in the thread local
- * storage.
- * @param env the JNI environment
- */
-void JNIUtil::setEnv(JNIEnv *env)
-{
- JNIThreadData::pushNewThreadData();
- JNIThreadData *data = JNIThreadData::getThreadData();
- data->m_env = env;
- data->m_exceptionThrown = false;
-}
+ jstring jclass_name;
+ {
+ jmethodID mid = env->GetMethodID(cls, "getClass", "()Ljava/lang/Class;");
+ jobject clsobj = env->CallObjectMethod(t, mid);
+ jclass basecls = env->GetObjectClass(clsobj);
+ mid = env->GetMethodID(basecls, "getName", "()Ljava/lang/String;");
+ jclass_name = (jstring) env->CallObjectMethod(clsobj, mid);
+ }
-/**
- * Return the JNI environment to use
- * @return the JNI environment
- */
-JNIEnv *JNIUtil::getEnv()
-{
- // During init -> look into the global variable.
- if (g_inInit)
- return g_initEnv;
+ jstring jmessage;
+ {
+ jmethodID mid = env->GetMethodID(cls, "getMessage",
+ "()Ljava/lang/String;");
+ jmessage = (jstring) env->CallObjectMethod(t, mid);
+ }
- // Look in the thread local storage.
- JNIThreadData *data = JNIThreadData::getThreadData();
- return data->m_env;
+ JNIStringHolder class_name(jclass_name);
+ if (jmessage)
+ {
+ JNIStringHolder message(jmessage);
+ return apr_pstrcat(pool, class_name.c_str(), ": ", message.c_str(), NULL);
+ }
+ else
+ return class_name.pstrdup(pool);
+ // ### Conditionally add t.printStackTrace() to msg?
}
-/**
- * Check if a Java exception has been thrown.
- * @return is a Java exception has been thrown
- */
-bool JNIUtil::isJavaExceptionThrown()
+const char* exception_to_cstring(apr_pool_t* pool)
{
- JNIEnv *env = getEnv();
- if (env->ExceptionCheck())
+ const char *msg;
+ if (JNIUtil::getEnv()->ExceptionCheck())
{
- // Retrieving the exception removes it so we rethrow it here.
- jthrowable exp = env->ExceptionOccurred();
- env->ExceptionDescribe();
- env->Throw(exp);
- env->DeleteLocalRef(exp);
- setExceptionThrown();
- return true;
+ msg = known_exception_to_cstring(pool);
}
- return false;
+ else
+ {
+ msg = NULL;
+ }
+ return msg;
}
+} // anonymous namespace
const char *
JNIUtil::thrownExceptionToCString(SVN::Pool &in_pool)
{
- const char *msg = NULL;
- JNIEnv *env = getEnv();
- apr_pool_t *pool = in_pool.getPool();
- if (env->ExceptionCheck())
- {
- jthrowable t = env->ExceptionOccurred();
- jclass cls = env->GetObjectClass(t);
-
- jstring jclass_name;
- {
- jmethodID mid = env->GetMethodID(cls, "getClass", "()Ljava/lang/Class;");
- jobject clsobj = env->CallObjectMethod(t, mid);
- jclass basecls = env->GetObjectClass(clsobj);
- mid = env->GetMethodID(basecls, "getName", "()Ljava/lang/String;");
- jclass_name = (jstring) env->CallObjectMethod(clsobj, mid);
- }
-
- jstring jmessage;
- {
- jmethodID mid = env->GetMethodID(cls, "getMessage",
- "()Ljava/lang/String;");
- jmessage = (jstring) env->CallObjectMethod(t, mid);
- }
+ return exception_to_cstring(in_pool.getPool());
+}
- JNIStringHolder class_name(jclass_name);
- if (jmessage)
- {
- JNIStringHolder message(jmessage);
- msg = apr_pstrcat(pool,
- static_cast<const char*>(class_name), ": ",
- static_cast<const char*>(message), NULL);
- }
- else
- msg = class_name.pstrdup(pool);
- // ### Conditionally add t.printStackTrace() to msg?
- }
- return msg;
+svn_error_t*
+JNIUtil::checkJavaException(apr_status_t errorcode)
+{
+ if (!getEnv()->ExceptionCheck())
+ return SVN_NO_ERROR;
+ svn_error_t* err = svn_error_create(errorcode, NULL, NULL);
+ const char* const msg = known_exception_to_cstring(err->pool);
+ if (msg)
+ err->message = apr_psprintf(err->pool, _("Java exception: %s"), msg);
+ else
+ err->message = _("Java exception");
+ return err;
}
/**
@@ -710,25 +841,6 @@ jstring JNIUtil::makeJString(const char *txt)
return env->NewStringUTF(txt);
}
-void
-JNIUtil::setExceptionThrown(bool flag)
-{
- if (g_inInit)
- {
- // During global initialization, store any errors that occur
- // in a global variable (since thread-local storage may not
- // yet be available).
- g_initException = flag;
- }
- else
- {
- // When global initialization is complete, thread-local
- // storage should be available, so store the error there.
- JNIThreadData *data = JNIThreadData::getThreadData();
- data->m_exceptionThrown = flag;
- }
-}
-
/**
* Initialite the log file.
* @param level the log level
@@ -752,23 +864,6 @@ void JNIUtil::initLogFile(int level, jstring path)
}
/**
- * Returns a buffer to format error messages.
- * @return a buffer for formating error messages
- */
-char *JNIUtil::getFormatBuffer()
-{
- if (g_inInit) // during init -> use the global buffer
- return g_initFormatBuffer;
-
- // use the buffer in the thread local storage
- JNIThreadData *data = JNIThreadData::getThreadData();
- if (data == NULL) // if that does not exists -> use the global buffer
- return g_initFormatBuffer;
-
- return data->m_formatBuffer;
-}
-
-/**
* Returns the current log level.
* @return the log level
*/
@@ -819,6 +914,31 @@ jobject JNIUtil::createDate(apr_time_t time)
return ret;
}
+apr_time_t
+JNIUtil::getDate(jobject jdate)
+{
+ JNIEnv *env = getEnv();
+ jclass clazz = env->FindClass("java/util/Date");
+ if (isJavaExceptionThrown())
+ return 0;
+
+ static jmethodID mid = 0;
+ if (mid == 0)
+ {
+ mid = env->GetMethodID(clazz, "getTime", "()J");
+ if (isJavaExceptionThrown())
+ return 0;
+ }
+
+ jlong jmillis = env->CallLongMethod(jdate, mid);
+ if (isJavaExceptionThrown())
+ return 0;
+
+ env->DeleteLocalRef(clazz);
+
+ return jmillis * 1000;
+}
+
/**
* Create a Java byte array from an array of characters.
* @param data the character array
@@ -826,11 +946,9 @@ jobject JNIUtil::createDate(apr_time_t time)
*/
jbyteArray JNIUtil::makeJByteArray(const void *data, int length)
{
- if (data == NULL)
- {
- // a NULL will create no Java array
- return NULL;
- }
+ // a NULL will create no Java array
+ if (!data)
+ return NULL;
JNIEnv *env = getEnv();
@@ -861,62 +979,11 @@ jbyteArray JNIUtil::makeJByteArray(const void *data, int length)
*/
jbyteArray JNIUtil::makeJByteArray(const svn_string_t *str)
{
- return JNIUtil::makeJByteArray(str->data, static_cast<int>(str->len));
-}
-
-/**
- * Build the error message from the svn error into buffer. This
- * method calls itselft recursively for all the chained errors
- *
- * @param err the subversion error
- * @param depth the depth of the call, used for formating
- * @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
- */
-void JNIUtil::assembleErrorMessage(svn_error_t *err, int depth,
- apr_status_t parent_apr_err,
- std::string &buffer)
-{
- // buffer for a single error message
- char errbuf[256];
-
- /* Pretty-print the error */
- /* Note: we can also log errors here someday. */
-
- /* When we're recursing, don't repeat the top-level message if its
- * the same as before. */
- if (depth == 0 || err->apr_err != parent_apr_err)
- {
- /* 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)));
- /* 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);
- if (utf8_err)
- {
- /* Use fuzzy transliteration instead. */
- svn_error_clear(utf8_err);
- utf8_message = svn_utf_cstring_from_utf8_fuzzy(errbuf, err->pool);
- }
- buffer.append(utf8_message);
- }
- buffer.append("\n");
- }
- if (err->message)
- buffer.append(_("svn: ")).append(err->message).append("\n");
-
- if (err->child)
- assembleErrorMessage(err->child, depth + 1, err->apr_err, buffer);
+ // a NULL will create no Java array
+ if (!str)
+ return NULL;
+ return JNIUtil::makeJByteArray(str->data, static_cast<int>(str->len));
}
/**
@@ -936,8 +1003,6 @@ void JNIUtil::throwNullPointerException(const char *message)
return;
env->ThrowNew(clazz, message);
- setExceptionThrown();
- env->DeleteLocalRef(clazz);
}
svn_error_t *JNIUtil::preprocessPath(const char *&path, apr_pool_t *pool)
@@ -996,3 +1061,91 @@ svn_error_t *JNIUtil::preprocessPath(const char *&path, apr_pool_t *pool)
return NULL;
}
+
+/* Tag to use on the apr_pool_t to store a WrappedException reference */
+static const char *WrapExceptionTag = "org.apache.subversion.JavaHL.svnerror";
+
+class WrappedException
+{
+ JNIEnv *m_env;
+ jthrowable m_exception;
+#ifdef SVN_DEBUG
+ bool m_fetched;
+#endif
+public:
+ WrappedException(JNIEnv *env)
+ {
+ m_env = env;
+
+ // Fetch exception inside local frame
+ jthrowable exceptionObj = env->ExceptionOccurred();
+
+ // Now clear exception status
+ env->ExceptionClear();
+
+ // As adding a reference in exception state fails
+ m_exception = static_cast<jthrowable>(env->NewGlobalRef(exceptionObj));
+
+#ifdef SVN_DEBUG
+ m_fetched = false;
+#endif
+ }
+
+ static jthrowable get_exception(apr_pool_t *pool)
+ {
+ void *data;
+ if (! apr_pool_userdata_get(&data, WrapExceptionTag, pool))
+ {
+ WrappedException *we = reinterpret_cast<WrappedException *>(data);
+
+ if (we)
+ {
+#ifdef SVN_DEBUG
+ we->m_fetched = TRUE;
+#endif
+ // Create reference in local frame, as the pool will be cleared
+ return static_cast<jthrowable>(
+ we->m_env->NewLocalRef(we->m_exception));
+ }
+ }
+ return NULL;
+ }
+
+private:
+ ~WrappedException()
+ {
+#ifdef SVN_DEBUG
+ if (!m_fetched)
+ SVN_DBG(("Cleared svn_error_t * before Java exception was fetched"));
+#endif
+ m_env->DeleteGlobalRef(m_exception);
+ }
+public:
+ static apr_status_t cleanup(void *data)
+ {
+ WrappedException *we = reinterpret_cast<WrappedException *>(data);
+
+ delete we;
+ return APR_SUCCESS;
+ }
+};
+
+svn_error_t* JNIUtil::wrapJavaException()
+{
+ if (!isExceptionThrown())
+ return SVN_NO_ERROR;
+
+ svn_error_t *err = svn_error_create(SVN_ERR_JAVAHL_WRAPPED, NULL,
+ "Wrapped Java Exception");
+ apr_pool_userdata_set(new WrappedException(getEnv()), WrapExceptionTag,
+ WrappedException::cleanup, err->pool);
+ return err;
+}
+
+jthrowable JNIUtil::unwrapJavaException(const svn_error_t *err)
+{
+ if (!err)
+ return NULL;
+ return
+ WrappedException::get_exception(err->pool);
+}
diff --git a/subversion/bindings/javahl/native/JNIUtil.h b/subversion/bindings/javahl/native/JNIUtil.h
index d353984..e05d1ef 100644
--- a/subversion/bindings/javahl/native/JNIUtil.h
+++ b/subversion/bindings/javahl/native/JNIUtil.h
@@ -37,11 +37,29 @@ class SVNBase;
#include <fstream>
#include <apr_time.h>
#include <string>
+#include <vector>
+
struct svn_error_t;
+struct svn_string_t;
-#define JAVA_PACKAGE "org/apache/subversion/javahl"
+#include "svn_error.h"
+
+
+/**
+ * The name of the package in which the JavaHL classes are defined.
+ */
+#define JAVAHL_PACKAGE "org/apache/subversion/javahl"
+
+/**
+ * Construct a JavaHL class name for JNIEnv::FindClass.
+ */
+#define JAVAHL_CLASS(name) JAVAHL_PACKAGE name
+
+/**
+ * Construct a JavaHL class parameter name for JNIEnv::GetMethodID & co.
+ */
+#define JAVAHL_ARG(name) "L" JAVAHL_PACKAGE name
-struct svn_string_t;
/**
* Class to hold a number of JNI related utility methods. No Objects
@@ -67,19 +85,24 @@ class JNIUtil
static jbyteArray makeJByteArray(const void *data, int length);
static jbyteArray makeJByteArray(const svn_string_t *str);
static jobject createDate(apr_time_t time);
+ static apr_time_t getDate(jobject jdate);
static void logMessage(const char *message);
static int getLogLevel();
- static char *getFormatBuffer();
static void initLogFile(int level, jstring path);
static jstring makeJString(const char *txt);
- static bool isJavaExceptionThrown();
static JNIEnv *getEnv();
- static void setEnv(JNIEnv *);
/**
* @return Whether any Throwable has been raised.
*/
- static bool isExceptionThrown();
+ static bool isExceptionThrown() { return isJavaExceptionThrown(); }
+ static bool isJavaExceptionThrown()
+ {
+ return getEnv()->ExceptionCheck();
+ }
+
+ static svn_error_t *wrapJavaException();
+ static jthrowable unwrapJavaException(const svn_error_t *err);
static void handleAPRError(int error, const char *op);
@@ -107,12 +130,27 @@ class JNIUtil
static const char *thrownExceptionToCString(SVN::Pool &in_pool);
/**
+ * Check if a Java exception was thrown and convert it to a
+ * Subversion error, using @a errorcode as the generic error code.
+ */
+ static svn_error_t* checkJavaException(apr_status_t errorcode);
+
+ /**
+ * Create a Java exception corresponding to err, and run
+ * svn_error_clear() on err.
+ */
+ static jthrowable createClientException(svn_error_t *err,
+ jthrowable jcause = NULL);
+
+ /**
* 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 jstring makeSVNErrorMessage(svn_error_t *err);
+ static std::string makeSVNErrorMessage(svn_error_t *err,
+ jstring *jerror_message,
+ jobject *jmessage_stack);
/**
* Create and throw a java.lang.Throwable instance.
@@ -130,28 +168,27 @@ class JNIUtil
*/
static void throwError(const char *message)
{
- raiseThrowable(JAVA_PACKAGE"/JNIError", message);
+ raiseThrowable(JAVAHL_CLASS("/JNIError"), message);
}
static apr_pool_t *getPool();
- static bool JNIGlobalInit(JNIEnv *env);
static bool JNIInit(JNIEnv *env);
static bool initializeJNIRuntime();
- enum { formatBufferSize = 2048 };
enum { noLog, errorLog, exceptionLog, entryLog } LogLevel;
+ /**
+ * Mutex that secures the global configuration object.
+ */
+ static JNIMutex *g_configMutex;
+
private:
- static void wrappedHandleSVNError(svn_error_t *err);
- static void assembleErrorMessage(svn_error_t *err, int depth,
- apr_status_t parent_apr_err,
- std::string &buffer);
+ friend bool initialize_jni_util(JNIEnv *env);
+ static bool JNIGlobalInit(JNIEnv *env);
+
+ static jthrowable wrappedCreateClientException(svn_error_t *err,
+ jthrowable jcause);
static void putErrorsInTrace(svn_error_t *err,
std::vector<jobject> &stackTrace);
- /**
- * Set the appropriate global or thread-local flag that an exception
- * has been thrown to @a flag.
- */
- static void setExceptionThrown(bool flag = true);
/**
* The log level of this module.
@@ -185,22 +222,6 @@ class JNIUtil
static bool g_initException;
/**
- * Flag, that one thread is in the init code. Cannot use mutex
- * here since apr is not initialized yet.
- */
- static bool g_inInit;
-
- /**
- * The JNI environment used during initialization.
- */
- static JNIEnv *g_initEnv;
-
- /**
- * Fuffer the format error messages during initialization.
- */
- static char g_initFormatBuffer[formatBufferSize];
-
- /**
* The stream to write log messages to.
*/
static std::ofstream g_logStream;
@@ -270,6 +291,16 @@ class JNIUtil
} \
while (0)
+#define POP_AND_RETURN_EXCEPTION_AS_SVNERROR() \
+ do \
+ { \
+ svn_error_t *svn__err_for_exception = JNIUtil::wrapJavaException(); \
+ \
+ env->PopLocalFrame(NULL); \
+ return svn__err_for_exception; \
+ } \
+ while (0)
+
/**
* A useful macro.
@@ -284,4 +315,19 @@ class JNIUtil
} \
} while (0)
+#define SVN_JNI_CATCH(statement, errorcode) \
+ do { \
+ do { statement; } while(0); \
+ SVN_ERR(JNIUtil::checkJavaException((errorcode))); \
+ } while(0)
+
+#define SVN_JNI_CATCH_VOID(statement) \
+ do { \
+ do { statement; } while(0); \
+ if (JNIUtil::getEnv()->ExceptionCheck()) { \
+ JNIUtil::getEnv()->ExceptionClear(); \
+ return; \
+ } \
+ } while(0)
+
#endif // JNIUTIL_H
diff --git a/subversion/bindings/javahl/native/ListCallback.cpp b/subversion/bindings/javahl/native/ListCallback.cpp
index 9759ff6..1cd089d 100644
--- a/subversion/bindings/javahl/native/ListCallback.cpp
+++ b/subversion/bindings/javahl/native/ListCallback.cpp
@@ -87,13 +87,13 @@ ListCallback::doList(const char *path,
static jmethodID mid = 0;
if (mid == 0)
{
- jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/ListCallback");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/ListCallback"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN(SVN_NO_ERROR);
mid = env->GetMethodID(clazz, "doEntry",
- "(L"JAVA_PACKAGE"/types/DirEntry;"
- "L"JAVA_PACKAGE"/types/Lock;)V");
+ "(" JAVAHL_ARG("/types/DirEntry;")
+ JAVAHL_ARG("/types/Lock;") ")V");
if (JNIUtil::isJavaExceptionThrown() || mid == 0)
POP_AND_RETURN(SVN_NO_ERROR);
}
@@ -113,10 +113,8 @@ ListCallback::doList(const char *path,
// call the Java method
env->CallVoidMethod(m_callback, mid, jdirentry, jlock);
- // No need to check for exception here, because we'll just return anyway
- env->PopLocalFrame(NULL);
- return SVN_NO_ERROR;
+ POP_AND_RETURN_EXCEPTION_AS_SVNERROR();
}
/**
@@ -126,53 +124,5 @@ jobject
ListCallback::createJavaDirEntry(const char *path, const char *absPath,
const svn_dirent_t *dirent)
{
- JNIEnv *env = JNIUtil::getEnv();
-
- // Create a local frame for our references
- env->PushLocalFrame(LOCAL_FRAME_SIZE);
- if (JNIUtil::isJavaExceptionThrown())
- return SVN_NO_ERROR;
-
- jclass clazz = env->FindClass(JAVA_PACKAGE"/types/DirEntry");
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
-
- static jmethodID mid = 0;
- if (mid == 0)
- {
- mid = env->GetMethodID(clazz, "<init>",
- "(Ljava/lang/String;Ljava/lang/String;"
- "L"JAVA_PACKAGE"/types/NodeKind;"
- "JZJJLjava/lang/String;)V");
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
- }
-
- jstring jPath = JNIUtil::makeJString(path);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
-
- jstring jAbsPath = JNIUtil::makeJString(absPath);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
-
- jobject jNodeKind = EnumMapper::mapNodeKind(dirent->kind);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
-
- jlong jSize = dirent->size;
- jboolean jHasProps = (dirent->has_props? JNI_TRUE : JNI_FALSE);
- jlong jLastChangedRevision = dirent->created_rev;
- jlong jLastChanged = dirent->time;
- jstring jLastAuthor = JNIUtil::makeJString(dirent->last_author);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
-
- jobject ret = env->NewObject(clazz, mid, jPath, jAbsPath, jNodeKind,
- jSize, jHasProps, jLastChangedRevision,
- jLastChanged, jLastAuthor);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
-
- return env->PopLocalFrame(ret);
+ return CreateJ::DirEntry(path, absPath, dirent);
}
diff --git a/subversion/bindings/javahl/native/LockTokenTable.cpp b/subversion/bindings/javahl/native/LockTokenTable.cpp
new file mode 100644
index 0000000..ebf50da
--- /dev/null
+++ b/subversion/bindings/javahl/native/LockTokenTable.cpp
@@ -0,0 +1,114 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file LockTokenTable.cpp
+ * @brief Implementation of the class LockTokenTable
+ */
+
+#include "LockTokenTable.h"
+#include "JNIUtil.h"
+#include "JNIStringHolder.h"
+#include "Array.h"
+#include <apr_hash.h>
+
+LockTokenTable::~LockTokenTable()
+{
+ if (m_jlock_tokens)
+ JNIUtil::getEnv()->DeleteLocalRef(m_jlock_tokens);
+}
+
+apr_hash_t*
+LockTokenTable::hash(const SVN::Pool &pool, bool null_if_empty)
+{
+ if (m_lock_tokens.size() == 0 && null_if_empty)
+ return NULL;
+
+ apr_pool_t* result_pool = pool.getPool();
+ apr_hash_t* lock_table = apr_hash_make(result_pool);
+
+ for (lock_tokens_t::const_iterator it = m_lock_tokens.begin();
+ it != m_lock_tokens.end(); ++it)
+ {
+ const char *path = apr_pstrdup(result_pool, it->first.c_str());
+ const char *token = apr_pstrdup(result_pool, it->second.c_str());
+ apr_hash_set(lock_table, path, APR_HASH_KEY_STRING, token);
+ }
+
+ return lock_table;
+}
+
+LockTokenTable::LockTokenTable(jobject jlock_tokens)
+ : m_jlock_tokens(jlock_tokens)
+{
+
+ if (jlock_tokens != NULL)
+ {
+ JNIEnv *env = JNIUtil::getEnv();
+
+ jclass lock_cls = env->FindClass(JAVAHL_CLASS("/types/Lock"));
+ if (JNIUtil::isExceptionThrown())
+ return;
+
+ static jmethodID getPath_mid = 0;
+ if (0 == getPath_mid)
+ {
+ getPath_mid = env->GetMethodID(lock_cls, "getPath",
+ "()Ljava/lang/String;");
+ if (JNIUtil::isExceptionThrown())
+ return;
+ }
+
+ static jmethodID getToken_mid = 0;
+ if (0 == getToken_mid)
+ {
+ getToken_mid = env->GetMethodID(lock_cls, "getToken",
+ "()Ljava/lang/String;");
+ if (JNIUtil::isExceptionThrown())
+ return;
+ }
+
+ std::vector<jobject> locks = Array(jlock_tokens).vector();
+ for (std::vector<jobject>::const_iterator it = locks.begin();
+ it != locks.end(); ++it)
+ {
+ jobject jpath = env->CallObjectMethod(*it, getPath_mid);
+ if (JNIUtil::isExceptionThrown())
+ return;
+ jobject jtoken = env->CallObjectMethod(*it, getToken_mid);
+ if (JNIUtil::isExceptionThrown())
+ return;
+
+ JNIStringHolder path((jstring)jpath);
+ if (JNIUtil::isExceptionThrown())
+ return;
+ JNIStringHolder token((jstring)jtoken);
+ if (JNIUtil::isExceptionThrown())
+ return;
+
+ m_lock_tokens[std::string(static_cast<const char *>(path))] =
+ std::string(static_cast<const char *>(token));
+
+ env->DeleteLocalRef(jpath);
+ env->DeleteLocalRef(jtoken);
+ }
+ }
+}
diff --git a/subversion/bindings/javahl/native/LockTokenTable.h b/subversion/bindings/javahl/native/LockTokenTable.h
new file mode 100644
index 0000000..af8b32a
--- /dev/null
+++ b/subversion/bindings/javahl/native/LockTokenTable.h
@@ -0,0 +1,50 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file LockTokenTable.h
+ * @brief Interface of the class LockTokenTable
+ */
+
+#ifndef JAVAHL_LOCK_TOKEN_TABLE_H
+#define JAVAHL_LOCK_TOKEN_TABLE_H
+
+#include <jni.h>
+#include "Pool.h"
+
+struct apr_hash_t;
+
+#include <map>
+#include <string>
+
+class LockTokenTable
+{
+ private:
+ typedef std::map<std::string, std::string> lock_tokens_t;
+ lock_tokens_t m_lock_tokens;
+ jobject m_jlock_tokens;
+ public:
+ LockTokenTable(jobject jlock_tokens);
+ ~LockTokenTable();
+ apr_hash_t *hash(const SVN::Pool &pool, bool null_if_empty = true);
+};
+
+#endif // JAVAHL_LOCK_TOKEN_TABLE_H
diff --git a/subversion/bindings/javahl/native/LogMessageCallback.cpp b/subversion/bindings/javahl/native/LogMessageCallback.cpp
index 237aabf..ffda075 100644
--- a/subversion/bindings/javahl/native/LogMessageCallback.cpp
+++ b/subversion/bindings/javahl/native/LogMessageCallback.cpp
@@ -81,7 +81,7 @@ LogMessageCallback::singleMessage(svn_log_entry_t *log_entry, apr_pool_t *pool)
static jmethodID sm_mid = 0;
if (sm_mid == 0)
{
- jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/LogMessageCallback");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/LogMessageCallback"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN(SVN_NO_ERROR);
@@ -103,9 +103,9 @@ LogMessageCallback::singleMessage(svn_log_entry_t *log_entry, apr_pool_t *pool)
hi = apr_hash_next(hi))
{
const char *path =
- reinterpret_cast<const char *>(svn__apr_hash_index_key(hi));
+ reinterpret_cast<const char *>(apr_hash_this_key(hi));
svn_log_changed_path2_t *log_item =
- reinterpret_cast<svn_log_changed_path2_t *>(svn__apr_hash_index_val(hi));
+ reinterpret_cast<svn_log_changed_path2_t *>(apr_hash_this_val(hi));
jobject cp = CreateJ::ChangedPath(path, log_item);
@@ -117,7 +117,7 @@ LogMessageCallback::singleMessage(svn_log_entry_t *log_entry, apr_pool_t *pool)
jobject jrevprops = NULL;
if (log_entry->revprops != NULL && apr_hash_count(log_entry->revprops) > 0)
- jrevprops = CreateJ::PropertyMap(log_entry->revprops);
+ jrevprops = CreateJ::PropertyMap(log_entry->revprops, pool);
env->CallVoidMethod(m_callback,
sm_mid,
@@ -125,8 +125,6 @@ LogMessageCallback::singleMessage(svn_log_entry_t *log_entry, apr_pool_t *pool)
(jlong)log_entry->revision,
jrevprops,
(jboolean)log_entry->has_children);
- // No need to check for an exception here, because we return anyway.
- env->PopLocalFrame(NULL);
- return SVN_NO_ERROR;
+ POP_AND_RETURN_EXCEPTION_AS_SVNERROR();
}
diff --git a/subversion/bindings/javahl/native/MessageReceiver.cpp b/subversion/bindings/javahl/native/MessageReceiver.cpp
index 18f695d..330c527 100644
--- a/subversion/bindings/javahl/native/MessageReceiver.cpp
+++ b/subversion/bindings/javahl/native/MessageReceiver.cpp
@@ -58,7 +58,7 @@ void MessageReceiver::receiveMessage(const char *message)
static jmethodID mid = 0;
if (mid == 0)
{
- jclass clazz = env->FindClass(JAVA_PACKAGE"/ISVNAdmin$MessageReceiver");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/ISVNAdmin$MessageReceiver"));
if (JNIUtil::isJavaExceptionThrown())
return;
diff --git a/subversion/bindings/javahl/native/NativeStream.cpp b/subversion/bindings/javahl/native/NativeStream.cpp
new file mode 100644
index 0000000..193c36f
--- /dev/null
+++ b/subversion/bindings/javahl/native/NativeStream.cpp
@@ -0,0 +1,381 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#include "NativeStream.hpp"
+
+#include "jniwrapper/jni_stack.hpp"
+#include "jniwrapper/jni_exception.hpp"
+
+#include "svn_private_config.h"
+
+namespace JavaHL {
+
+// Class JavaHL::NativeInputStream
+
+const char* const NativeInputStream::m_class_name =
+ JAVAHL_CLASS("/types/NativeInputStream");
+
+NativeInputStream::~NativeInputStream() {}
+
+void NativeInputStream::set_stream(svn_stream_t* stream)
+{
+ if (m_stream)
+ throw std::logic_error(_("Native input stream is already bound"));
+ m_stream = stream;
+}
+
+NativeInputStream*
+NativeInputStream::get_self_unsafe(::Java::Env env, jobject jthis)
+{
+ jfieldID fid_cppaddr = NULL;
+ const jlong cppaddr =
+ findCppAddrForJObject(jthis, &fid_cppaddr, m_class_name);
+ return reinterpret_cast<NativeInputStream*>(cppaddr);
+}
+
+NativeInputStream*
+NativeInputStream::get_self(::Java::Env env, jobject jthis)
+{
+ NativeInputStream* self = get_self_unsafe(env, jthis);
+ if (!self)
+ ::Java::NullPointerException(env).raise(_("this [C++]"));
+ return self;
+}
+
+void NativeInputStream::close(::Java::Env env, jobject jthis)
+{
+ SVN_JAVAHL_CHECK(env, svn_stream_close(m_stream));
+ dispose(jthis);
+}
+
+bool NativeInputStream::mark_supported(::Java::Env env) const
+{
+ return svn_stream_supports_mark(m_stream);
+}
+
+void NativeInputStream::mark(::Java::Env env)
+{
+ if (!svn_stream_supports_mark(m_stream))
+ return;
+ SVN_JAVAHL_CHECK(env, svn_stream_mark(m_stream, &m_mark, pool.getPool()));
+}
+
+void NativeInputStream::reset(::Java::Env env)
+{
+ if (!svn_stream_supports_mark(m_stream))
+ return;
+ if (m_mark)
+ SVN_JAVAHL_CHECK(env, svn_stream_seek(m_stream, m_mark));
+ else
+ ::Java::IOException(env).raise(_("Invalid seek on native stream"));
+ }
+
+jint NativeInputStream::read(::Java::Env env)
+{
+ apr_size_t len = 1;
+ char byte;
+ SVN_JAVAHL_CHECK(env, svn_stream_read_full(m_stream, &byte, &len));
+ if (len == 0)
+ return -1; // EOF
+ if (len == 1)
+ return jint(byte & 0xff);
+ ::Java::IOException(env).raise(_("Read from native stream failed"));
+ return -1;
+}
+
+jint NativeInputStream::read(::Java::Env env,
+ ::Java::ByteArray::MutableContents& dst,
+ jint offset, jint length)
+{
+ if (offset < 0 || length < 0 || offset + length > dst.length())
+ ::Java::IndexOutOfBoundsException(env).raise();
+ if (!dst.data())
+ ::Java::NullPointerException(env).raise();
+
+ apr_size_t len = length;
+ if (svn_stream_supports_partial_read(m_stream))
+ SVN_JAVAHL_CHECK(env, svn_stream_read2(m_stream,
+ dst.data() + offset, &len));
+ else
+ SVN_JAVAHL_CHECK(env, svn_stream_read_full(m_stream,
+ dst.data() + offset, &len));
+ if (len == 0)
+ return -1; // EOF
+ if (len <= length)
+ return jint(len);
+ ::Java::IOException(env).raise(_("Read from native stream failed"));
+ return -1;
+}
+
+jlong NativeInputStream::skip(::Java::Env env, jlong count)
+{
+ const apr_size_t len = count;
+ SVN_JAVAHL_CHECK(env, svn_stream_skip(m_stream, len));
+ return count;
+}
+
+void NativeInputStream::dispose(jobject jthis)
+{
+ jfieldID fid_cppaddr = NULL;
+ SVNBase::dispose(jthis, &fid_cppaddr, m_class_name);
+}
+
+
+// Class JavaHL::NativeOutputStream
+
+const char* const NativeOutputStream::m_class_name =
+ JAVAHL_CLASS("/types/NativeOutputStream");
+
+NativeOutputStream::~NativeOutputStream() {}
+
+void NativeOutputStream::set_stream(svn_stream_t* stream)
+{
+ if (m_stream)
+ throw std::logic_error(_("Native output stream is already bound"));
+ m_stream = stream;
+}
+
+NativeOutputStream*
+NativeOutputStream::get_self_unsafe(::Java::Env env, jobject jthis)
+{
+ jfieldID fid_cppaddr = NULL;
+ const jlong cppaddr =
+ findCppAddrForJObject(jthis, &fid_cppaddr, m_class_name);
+ return reinterpret_cast<NativeOutputStream*>(cppaddr);
+}
+
+NativeOutputStream*
+NativeOutputStream::get_self(::Java::Env env, jobject jthis)
+{
+ NativeOutputStream* self = get_self_unsafe(env, jthis);
+ if (!self)
+ ::Java::NullPointerException(env).raise(_("this [C++]"));
+ return self;
+}
+
+void NativeOutputStream::close(::Java::Env env, jobject jthis)
+{
+ SVN_JAVAHL_CHECK(env, svn_stream_close(m_stream));
+ dispose(jthis);
+}
+
+void NativeOutputStream::write(::Java::Env env, jint byte)
+{
+ const char data = char(byte & 0xff);
+ apr_size_t len = 1;
+ SVN_JAVAHL_CHECK(env, svn_stream_write(m_stream, &data, &len));
+ if (len != 1)
+ ::Java::IOException(env).raise(_("Write to native stream failed"));
+}
+
+void NativeOutputStream::write(::Java::Env env,
+ const ::Java::ByteArray::Contents& src,
+ jint offset, jint length)
+{
+ if (offset < 0 || length < 0 || offset + length > src.length())
+ ::Java::IndexOutOfBoundsException(env).raise();
+ if (!src.data())
+ ::Java::NullPointerException(env).raise();
+
+ apr_size_t len = length;
+ SVN_JAVAHL_CHECK(env, svn_stream_write(m_stream, src.data() + offset, &len));
+ if (len != length)
+ ::Java::IOException(env).raise(_("Write to native stream failed"));
+}
+
+void NativeOutputStream::dispose(jobject jthis)
+{
+ jfieldID fid_cppaddr = NULL;
+ SVNBase::dispose(jthis, &fid_cppaddr, m_class_name);
+}
+
+} // namespace JavaHL
+
+
+// Class JavaHL::NativeInputStream native method implementation
+#include "../include/org_apache_subversion_javahl_types_NativeInputStream.h"
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_types_NativeInputStream_close(
+ JNIEnv* jenv, jobject jthis)
+{
+ SVN_JAVAHL_JNI_TRY(NativeInputStream, close)
+ {
+ SVN_JAVAHL_GET_BOUND_OBJECT(JavaHL::NativeInputStream, self);
+ self->close(Java::Env(jenv), jthis);
+ }
+ SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(Java::IOException);
+}
+
+JNIEXPORT jboolean JNICALL
+Java_org_apache_subversion_javahl_types_NativeInputStream_markSupported(
+ JNIEnv* jenv, jobject jthis)
+{
+ SVN_JAVAHL_JNI_TRY(NativeInputStream, markSupported)
+ {
+ SVN_JAVAHL_GET_BOUND_OBJECT(JavaHL::NativeInputStream, self);
+ self->mark_supported(Java::Env(jenv));
+ }
+ SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(Java::IOException);
+ return false;
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_types_NativeInputStream_mark(
+ JNIEnv* jenv, jobject jthis, jint)
+{
+ SVN_JAVAHL_JNI_TRY(NativeInputStream, mark)
+ {
+ SVN_JAVAHL_GET_BOUND_OBJECT(JavaHL::NativeInputStream, self);
+ self->mark(Java::Env(jenv));
+ }
+ SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(Java::IOException);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_types_NativeInputStream_reset(
+ JNIEnv* jenv, jobject jthis)
+{
+ SVN_JAVAHL_JNI_TRY(NativeInputStream, reset)
+ {
+ SVN_JAVAHL_GET_BOUND_OBJECT(JavaHL::NativeInputStream, self);
+ self->reset(Java::Env(jenv));
+ }
+ SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(Java::IOException);
+}
+
+JNIEXPORT jint JNICALL
+Java_org_apache_subversion_javahl_types_NativeInputStream_read__(
+ JNIEnv* jenv, jobject jthis)
+{
+ SVN_JAVAHL_JNI_TRY(NativeInputStream, read)
+ {
+ SVN_JAVAHL_GET_BOUND_OBJECT(JavaHL::NativeInputStream, self);
+ return self->read(Java::Env(jenv));
+ }
+ SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(Java::IOException);
+ return 0;
+}
+
+JNIEXPORT jint JNICALL
+Java_org_apache_subversion_javahl_types_NativeInputStream_read___3BII(
+ JNIEnv* jenv, jobject jthis, jbyteArray jdst, jint joffset, jint jlength)
+{
+ SVN_JAVAHL_JNI_TRY(NativeInputStream, read)
+ {
+ SVN_JAVAHL_GET_BOUND_OBJECT(JavaHL::NativeInputStream, self);
+
+ const Java::Env env(jenv);
+ Java::ByteArray dst(env, jdst);
+ Java::ByteArray::MutableContents dst_contents(dst);
+
+ return self->read(env, dst_contents, joffset, jlength);
+ }
+ SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(Java::IOException);
+ return 0;
+}
+
+JNIEXPORT jlong JNICALL
+Java_org_apache_subversion_javahl_types_NativeInputStream_skip(
+ JNIEnv* jenv, jobject jthis, jlong jcount)
+{
+ SVN_JAVAHL_JNI_TRY(NativeInputStream, skip)
+ {
+ SVN_JAVAHL_GET_BOUND_OBJECT(JavaHL::NativeInputStream, self);
+ return self->skip(Java::Env(jenv), jcount);
+ }
+ SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(Java::IOException);
+ return 0;
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_types_NativeInputStream_finalize(
+ JNIEnv* jenv, jobject jthis)
+{
+ SVN_JAVAHL_JNI_TRY(NativeInputStream, finalize)
+ {
+ JavaHL::NativeInputStream* native =
+ JavaHL::NativeInputStream::get_self_unsafe(Java::Env(jenv), jthis);
+ if (native != NULL)
+ native->finalize();
+ }
+ SVN_JAVAHL_JNI_CATCH;
+}
+
+
+// Class JavaHL::NativeOutputStream native method implementation
+#include "../include/org_apache_subversion_javahl_types_NativeOutputStream.h"
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_types_NativeOutputStream_close(
+ JNIEnv* jenv, jobject jthis)
+{
+ SVN_JAVAHL_JNI_TRY(NativeOutputStream, close)
+ {
+ SVN_JAVAHL_GET_BOUND_OBJECT(JavaHL::NativeOutputStream, self);
+ self->close(Java::Env(jenv), jthis);
+ }
+ SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(Java::IOException);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_types_NativeOutputStream_write__I(
+ JNIEnv* jenv, jobject jthis, jint byte)
+{
+ SVN_JAVAHL_JNI_TRY(NativeOutputStream, write)
+ {
+ SVN_JAVAHL_GET_BOUND_OBJECT(JavaHL::NativeOutputStream, self);
+ self->write(Java::Env(jenv), byte);
+ }
+ SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(Java::IOException);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_types_NativeOutputStream_write___3BII(
+ JNIEnv* jenv, jobject jthis, jbyteArray jsrc, jint joffset, jint jlength)
+{
+ SVN_JAVAHL_JNI_TRY(NativeOutputStream, write)
+ {
+ SVN_JAVAHL_GET_BOUND_OBJECT(JavaHL::NativeOutputStream, self);
+
+ const Java::Env env(jenv);
+ const Java::ByteArray src(env, jsrc);
+
+ self->write(env, Java::ByteArray::Contents(src), joffset, jlength);
+ }
+ SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(Java::IOException);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_types_NativeOutputStream_finalize(
+ JNIEnv* jenv, jobject jthis)
+{
+ SVN_JAVAHL_JNI_TRY(NativeOutputStream, finalize)
+ {
+ JavaHL::NativeOutputStream* native =
+ JavaHL::NativeOutputStream::get_self_unsafe(Java::Env(jenv), jthis);
+ if (native != NULL)
+ native->finalize();
+ }
+ SVN_JAVAHL_JNI_CATCH;
+}
diff --git a/subversion/bindings/javahl/native/NativeStream.hpp b/subversion/bindings/javahl/native/NativeStream.hpp
new file mode 100644
index 0000000..57fd137
--- /dev/null
+++ b/subversion/bindings/javahl/native/NativeStream.hpp
@@ -0,0 +1,211 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#ifndef SVN_JAVAHL_NATIVE_STREAM_HPP
+#define SVN_JAVAHL_NATIVE_STREAM_HPP
+
+#include <stdexcept>
+
+#include "jniwrapper/jni_array.hpp"
+
+#include "SVNBase.h"
+
+#include "svn_io.h"
+
+namespace JavaHL {
+
+/**
+ * Object wrapper for @c org.apache.subversion.javahl.types.NativeInputStream.
+ *
+ * @since New in 1.9.
+ */
+class NativeInputStream : public ::SVNBase
+{
+public:
+ /**
+ * Construcs the native instance that will be wrapped in a Java obejct.
+ * If @a stream is @c NULL, you must call #set_stream before creating
+ * the Java wrapper.
+ */
+ explicit NativeInputStream(svn_stream_t* stream = NULL)
+ : m_stream(stream),
+ m_mark(NULL)
+ {}
+
+ ~NativeInputStream();
+
+ /**
+ * Returns a reference to the pool owned by this wrapped object.
+ */
+ const SVN::Pool& get_pool() const
+ {
+ return pool;
+ }
+
+ /**
+ * Sets the @a stream that this object will own.
+ * Do not call this function if a stream was passed to the constructor.
+ */
+ void set_stream(svn_stream_t* stream);
+
+ /**
+ * Create the Java object that binds to this native object.
+ */
+ jobject create_java_wrapper()
+ {
+ return createCppBoundObject(m_class_name);
+ }
+
+ /**
+ * Retrieve the address of the native object from the bound Java object.
+ */
+ static NativeInputStream* get_self(::Java::Env env, jobject jthis);
+
+ static NativeInputStream* get_self_unsafe(::Java::Env env, jobject jthis);
+
+public:
+ /**
+ * Implements @c InputStream.close().
+ * Also disposes the native object.
+ */
+ void close(::Java::Env env, jobject jthis);
+
+ /**
+ * Implements @c InputStream.markSupported().
+ */
+ bool mark_supported(::Java::Env env) const;
+
+ /**
+ * Implements @c InputStream.mark(int).
+ * The @c readlimit parameter of the Java method is ignored.
+ */
+ void mark(::Java::Env env);
+
+ /**
+ * Implements @c InputStream.reset().
+ */
+ void reset(::Java::Env env);
+
+ /**
+ * Implements @c InputStream.read().
+ */
+ jint read(::Java::Env env);
+
+ /**
+ * Implements @c InputStream.read(byte[],int,int).
+ */
+ jint read(::Java::Env env,
+ ::Java::ByteArray::MutableContents& dst,
+ jint offset, jint length);
+
+ /**
+ * Implements @c InputStream.skip(long).
+ */
+ jlong skip(::Java::Env env, jlong count);
+
+private:
+ virtual void dispose(jobject jthis);
+
+ static const char* const m_class_name;
+ svn_stream_t* m_stream;
+ svn_stream_mark_t* m_mark;
+};
+
+
+/**
+ * Object wrapper for @c org.apache.subversion.javahl.types.NativeOutputStream.
+ *
+ * @since New in 1.9.
+ */
+class NativeOutputStream : public ::SVNBase
+{
+public:
+ /**
+ * Construcs the native instance that will be wrapped in a Java obejct.
+ * If @a stream is @c NULL, you must call #set_stream before creating
+ * the Java wrapper.
+ */
+ explicit NativeOutputStream(svn_stream_t* stream = NULL)
+ : m_stream(stream)
+ {}
+
+ ~NativeOutputStream();
+
+ /**
+ * Returns a reference to the pool owned by this wrapped object.
+ */
+ const SVN::Pool& get_pool() const
+ {
+ return pool;
+ }
+
+ /**
+ * Sets the @a stream that this object will own.
+ * Do not call this function if a stream was passed to the constructor.
+ */
+ void set_stream(svn_stream_t* stream);
+
+ /**
+ * Create the Java object that binds to this native object.
+ */
+ jobject create_java_wrapper()
+ {
+ return createCppBoundObject(m_class_name);
+ }
+
+ /**
+ * Retrieve the address of the native object from the bound Java object.
+ */
+ static NativeOutputStream* get_self(::Java::Env env, jobject jthis);
+
+ static NativeOutputStream* get_self_unsafe(::Java::Env env, jobject jthis);
+
+public:
+ /**
+ * Implements @c OutputStream.close().
+ * Also disposes the native object.
+ */
+ void close(::Java::Env env, jobject jthis);
+
+ /**
+ * Implements @c OutputStream.write(int).
+ */
+ void write(::Java::Env env, jint byte);
+
+ /**
+ * Implements @c OutputStream.write(byte[],int,int).
+ */
+ void write(::Java::Env env,
+ const ::Java::ByteArray::Contents& src,
+ jint offset, jint length);
+
+private:
+ virtual void dispose(jobject jthis);
+
+ static const char* const m_class_name;
+ svn_stream_t* m_stream;
+};
+
+} // namespace JavaHL
+
+#endif // SVN_JAVAHL_NATIVE_STREAM_HPP
diff --git a/subversion/bindings/javahl/native/OperationContext.cpp b/subversion/bindings/javahl/native/OperationContext.cpp
new file mode 100644
index 0000000..b455c36
--- /dev/null
+++ b/subversion/bindings/javahl/native/OperationContext.cpp
@@ -0,0 +1,657 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file OperationContext.cpp
+ * @brief Implementation of the class OperationContext
+ */
+
+#include <apr_file_io.h>
+
+#include "GlobalConfig.h"
+#include "OperationContext.h"
+#include "JNIUtil.h"
+#include "JNICriticalSection.h"
+
+#include "Prompter.h"
+#include "CreateJ.h"
+#include "EnumMapper.h"
+#include "CommitMessage.h"
+
+#include "svn_client.h"
+#include "private/svn_wc_private.h"
+#include "private/svn_dep_compat.h"
+#include "svn_private_config.h"
+
+OperationContext::OperationContext(SVN::Pool &pool)
+ : m_config(NULL),
+ m_prompter(NULL),
+ m_cancelOperation(0),
+ m_pool(&pool),
+ m_jctx(NULL),
+ m_jcfgcb(NULL),
+ m_jtunnelcb(NULL)
+{}
+
+void
+OperationContext::attachJavaObject(
+ jobject contextHolder, const char *contextClassType,
+ const char *contextFieldName, jfieldID * ctxFieldID)
+{
+ JNIEnv *env = JNIUtil::getEnv();
+
+ /* Grab a global reference to the Java object embedded in the parent
+ Java object. */
+ if ((*ctxFieldID) == 0)
+ {
+ jclass clazz = env->GetObjectClass(contextHolder);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ (*ctxFieldID) = env->GetFieldID(clazz, contextFieldName, contextClassType);
+ if (JNIUtil::isJavaExceptionThrown() || (*ctxFieldID) == 0)
+ return;
+
+ env->DeleteLocalRef(clazz);
+ }
+
+ jobject jctx = env->GetObjectField(contextHolder, (*ctxFieldID));
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ m_jctx = env->NewGlobalRef(jctx);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ env->DeleteLocalRef(jctx);
+}
+
+OperationContext::~OperationContext()
+{
+ JNIEnv *env = JNIUtil::getEnv();
+ env->DeleteGlobalRef(m_jctx);
+ if (m_jcfgcb)
+ env->DeleteGlobalRef(m_jcfgcb);
+ if (m_jtunnelcb)
+ env->DeleteGlobalRef(m_jtunnelcb);
+}
+
+apr_hash_t *
+OperationContext::getConfigData()
+{
+ if(m_pool->getPool() == NULL)
+ {
+ JNIUtil::throwNullPointerException("pool is null");
+ }
+
+ if (m_config == NULL)
+ {
+ const char *configDir = m_configDir.c_str();
+ if (m_configDir.empty())
+ configDir = NULL;
+ SVN_JNI_ERR(
+ svn_config_get_config(&m_config, configDir, m_pool->getPool()), NULL);
+ notifyConfigLoad();
+ }
+
+ return m_config;
+}
+
+svn_auth_baton_t *
+OperationContext::getAuthBaton(SVN::Pool &in_pool)
+{
+ svn_auth_baton_t *ab;
+ apr_pool_t *pool = in_pool.getPool();
+
+ apr_hash_t * configData = getConfigData();
+
+ if (configData == NULL)
+ {
+ return NULL;
+ }
+
+ svn_config_t *config = static_cast<svn_config_t *>(apr_hash_get(configData,
+ SVN_CONFIG_CATEGORY_CONFIG, APR_HASH_KEY_STRING));
+
+ const bool use_native_store = GlobalConfig::useNativeCredentialsStore();
+
+ /* The whole list of registered providers */
+ apr_array_header_t *providers;
+ svn_auth_provider_object_t *provider;
+
+ if (use_native_store)
+ {
+ /* Populate the registered providers with the platform-specific providers */
+ SVN_JNI_ERR(
+ svn_auth_get_platform_specific_client_providers(
+ &providers, config, pool),
+ NULL);
+
+ /* Use the prompter (if available) to prompt for password and cert
+ * caching. */
+ svn_auth_plaintext_prompt_func_t plaintext_prompt_func;
+ void *plaintext_prompt_baton;
+ svn_auth_plaintext_passphrase_prompt_func_t plaintext_passphrase_prompt_func;
+ void *plaintext_passphrase_prompt_baton;
+
+ if (m_prompter.get())
+ {
+ plaintext_prompt_func = Prompter::plaintext_prompt;
+ plaintext_prompt_baton = m_prompter.get();
+ plaintext_passphrase_prompt_func = Prompter::plaintext_passphrase_prompt;
+ plaintext_passphrase_prompt_baton = m_prompter.get();
+ }
+ else
+ {
+ plaintext_prompt_func = NULL;
+ plaintext_prompt_baton = NULL;
+ plaintext_passphrase_prompt_func = NULL;
+ plaintext_passphrase_prompt_baton = NULL;
+ }
+
+ /* The main disk-caching auth providers, for both
+ * 'username/password' creds and 'username' creds. */
+
+ svn_auth_get_simple_provider2(&provider, plaintext_prompt_func,
+ plaintext_prompt_baton, pool);
+ APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
+
+ svn_auth_get_username_provider(&provider, pool);
+ APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
+
+ svn_auth_get_ssl_server_trust_file_provider(&provider, pool);
+ APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
+ svn_auth_get_ssl_client_cert_file_provider(&provider, pool);
+ APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
+ svn_auth_get_ssl_client_cert_pw_file_provider2(
+ &provider,
+ plaintext_passphrase_prompt_func, plaintext_passphrase_prompt_baton,
+ pool);
+ APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
+ }
+ else
+ {
+ // Not using hte native credentials store, start with an empty
+ // providers array.
+ providers = apr_array_make(pool, 0, sizeof(svn_auth_provider_object_t *));
+ }
+
+ if (m_prompter.get())
+ {
+ /* Two basic prompt providers: username/password, and just username.*/
+ provider = m_prompter->get_provider_simple(in_pool);
+ APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
+
+ provider = m_prompter->get_provider_username(in_pool);
+ APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
+
+ /* Three ssl prompt providers, for server-certs, client-certs,
+ * and client-cert-passphrases. */
+ provider = m_prompter->get_provider_server_ssl_trust(in_pool);
+ APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
+
+ provider = m_prompter->get_provider_client_ssl(in_pool);
+ APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
+
+ provider = m_prompter->get_provider_client_ssl_password(in_pool);
+ APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
+ }
+
+ /* Build an authentication baton to give to libsvn_client. */
+ svn_auth_open(&ab, providers, pool);
+
+ /* Place any default --username or --password credentials into the
+ * auth_baton's run-time parameter hash. ### Same with --no-auth-cache? */
+ if (!m_userName.empty())
+ svn_auth_set_parameter(ab, SVN_AUTH_PARAM_DEFAULT_USERNAME,
+ apr_pstrdup(in_pool.getPool(), m_userName.c_str()));
+ if (!m_passWord.empty())
+ svn_auth_set_parameter(ab, SVN_AUTH_PARAM_DEFAULT_PASSWORD,
+ apr_pstrdup(in_pool.getPool(), m_passWord.c_str()));
+ /* Store where to retrieve authentication data? */
+ if (!m_configDir.empty())
+ svn_auth_set_parameter(ab, SVN_AUTH_PARAM_CONFIG_DIR,
+ apr_pstrdup(in_pool.getPool(), m_configDir.c_str()));
+ return ab;
+}
+
+jobject OperationContext::getSelf() const
+{
+ return m_jctx;
+}
+
+void
+OperationContext::username(const char *pi_username)
+{
+ m_userName = (pi_username == NULL ? "" : pi_username);
+}
+
+void
+OperationContext::password(const char *pi_password)
+{
+ m_passWord = (pi_password == NULL ? "" : pi_password);
+}
+
+void
+OperationContext::setPrompt(Prompter::UniquePtr prompter)
+{
+ m_prompter = prompter;
+}
+
+void
+OperationContext::setConfigDirectory(const char *configDir)
+{
+ // A change to the config directory may necessitate creation of
+ // the config templates.
+ SVN::Pool requestPool;
+ SVN_JNI_ERR(svn_config_ensure(configDir, requestPool.getPool()), );
+
+ m_configDir = (configDir == NULL ? "" : configDir);
+
+ m_config = NULL;
+}
+
+const char *
+OperationContext::getConfigDirectory() const
+{
+ return (m_configDir.empty() ? NULL : m_configDir.c_str());
+}
+
+void OperationContext::setConfigEventHandler(jobject jcfgcb)
+{
+ JNIEnv *env = JNIUtil::getEnv();
+ if (jcfgcb)
+ {
+ jcfgcb = env->NewGlobalRef(jcfgcb);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ }
+
+ if (m_jcfgcb)
+ env->DeleteGlobalRef(m_jcfgcb);
+ m_jcfgcb = jcfgcb;
+}
+
+jobject OperationContext::getConfigEventHandler() const
+{
+ return m_jcfgcb;
+}
+
+const char *
+OperationContext::getUsername() const
+{
+ return (m_userName.empty() ? NULL : m_userName.c_str());
+}
+
+const char *
+OperationContext::getPassword() const
+{
+ return (m_passWord.empty() ? NULL : m_passWord.c_str());
+}
+
+Prompter::UniquePtr OperationContext::clonePrompter() const
+{
+ if (m_prompter.get())
+ return m_prompter->clone();
+ return Prompter::UniquePtr(NULL);
+}
+
+void OperationContext::setTunnelCallback(jobject jtunnelcb)
+{
+ JNIEnv *env = JNIUtil::getEnv();
+ if (jtunnelcb)
+ {
+ jtunnelcb = env->NewGlobalRef(jtunnelcb);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ }
+
+ if (m_jtunnelcb)
+ env->DeleteGlobalRef(m_jtunnelcb);
+ m_jtunnelcb = jtunnelcb;
+}
+
+jobject OperationContext::getTunnelCallback() const
+{
+ return m_jtunnelcb;
+}
+
+void
+OperationContext::cancelOperation()
+{
+ svn_atomic_set(&m_cancelOperation, 1);
+}
+
+void
+OperationContext::resetCancelRequest()
+{
+ svn_atomic_set(&m_cancelOperation, 0);
+}
+
+bool
+OperationContext::isCancelledOperation()
+{
+ return bool(svn_atomic_read(&m_cancelOperation));
+}
+
+svn_error_t *
+OperationContext::checkCancel(void *cancelBaton)
+{
+ OperationContext *that = static_cast<OperationContext *>(cancelBaton);
+ if (that->isCancelledOperation())
+ return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Operation cancelled"));
+ else if (JNIUtil::isJavaExceptionThrown())
+ return svn_error_create(SVN_ERR_CANCELLED, JNIUtil::wrapJavaException(),
+ _("Operation cancelled"));
+ else
+ return SVN_NO_ERROR;
+}
+
+void
+OperationContext::progress(apr_off_t progressVal, apr_off_t total, void *baton,
+ apr_pool_t *pool)
+{
+ jobject jctx = (jobject) baton;
+ if (!jctx)
+ return;
+
+ JNIEnv *env = JNIUtil::getEnv();
+
+ // Create a local frame for our references
+ env->PushLocalFrame(LOCAL_FRAME_SIZE);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ static jmethodID mid = 0;
+ if (mid == 0)
+ {
+ jclass clazz = env->GetObjectClass(jctx);
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NOTHING();
+
+ mid = env->GetMethodID(clazz, "onProgress",
+ "(" JAVAHL_ARG("/ProgressEvent;") ")V");
+ if (JNIUtil::isJavaExceptionThrown() || mid == 0)
+ POP_AND_RETURN_NOTHING();
+ }
+
+ static jmethodID midCT = 0;
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/ProgressEvent"));
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NOTHING();
+
+ if (midCT == 0)
+ {
+ midCT = env->GetMethodID(clazz, "<init>", "(JJ)V");
+ if (JNIUtil::isJavaExceptionThrown() || midCT == 0)
+ POP_AND_RETURN_NOTHING();
+ }
+
+ // Call the Java method.
+ jobject jevent = env->NewObject(clazz, midCT, (jlong) progressVal,
+ (jlong) total);
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NOTHING();
+
+ env->CallVoidMethod(jctx, mid, jevent);
+
+ POP_AND_RETURN_NOTHING();
+}
+
+const char *
+OperationContext::getClientName() const
+{
+ return "javahl";
+}
+
+svn_error_t *
+OperationContext::clientName(void *baton, const char **name, apr_pool_t *pool)
+{
+ OperationContext *that = (OperationContext *) baton;
+
+ *name = that->getClientName();
+
+ return SVN_NO_ERROR;
+}
+
+void
+OperationContext::notifyConfigLoad()
+{
+ if (!m_jcfgcb)
+ return;
+
+ JNIEnv *env = JNIUtil::getEnv();
+
+ static jmethodID onload_mid = 0;
+ if (0 == onload_mid)
+ {
+ jclass cls = env->FindClass(JAVAHL_CLASS("/callback/ConfigEvent"));
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ onload_mid = env->GetMethodID(cls, "onLoad",
+ "(" JAVAHL_ARG("/ISVNConfig;") ")V");
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ }
+
+ jclass cfg_cls = env->FindClass(JAVAHL_CLASS("/util/ConfigImpl"));
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ static jmethodID ctor_mid = 0;
+ if (0 == ctor_mid)
+ {
+ ctor_mid = env->GetMethodID(cfg_cls, "<init>", "(J)V");
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ }
+
+ static jmethodID dispose_mid = 0;
+ if (0 == dispose_mid)
+ {
+ dispose_mid = env->GetMethodID(cfg_cls, "dispose", "()V");
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ }
+
+ jobject jcbimpl = env->NewObject(cfg_cls, ctor_mid,
+ reinterpret_cast<jlong>(this));
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ env->CallVoidMethod(m_jcfgcb, onload_mid, jcbimpl);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ env->CallVoidMethod(jcbimpl, dispose_mid);
+ env->DeleteLocalRef(jcbimpl);
+}
+
+namespace {
+class TunnelContext
+{
+public:
+ explicit TunnelContext(apr_pool_t *pool)
+ : request_in(NULL),
+ request_out(NULL),
+ response_in(NULL),
+ response_out(NULL),
+ jclosecb(NULL)
+ {
+ status = apr_file_pipe_create_ex(&request_in, &request_out,
+ APR_FULL_BLOCK, pool);
+ if (!status)
+ status = apr_file_pipe_create_ex(&response_in, &response_out,
+ APR_FULL_BLOCK, pool);
+ }
+
+ ~TunnelContext()
+ {
+ apr_file_close(request_out);
+ apr_file_close(response_in);
+ }
+
+ apr_file_t *request_in;
+ apr_file_t *request_out;
+ apr_file_t *response_in;
+ apr_file_t *response_out;
+ apr_status_t status;
+ jobject jclosecb;
+};
+
+jobject create_Channel(const char *class_name, JNIEnv *env, apr_file_t *fd)
+{
+ jclass cls = env->FindClass(class_name);
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+ jmethodID ctor = env->GetMethodID(cls, "<init>", "(J)V");
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+ return env->NewObject(cls, ctor, reinterpret_cast<jlong>(fd));
+}
+
+jobject create_RequestChannel(JNIEnv *env, apr_file_t *fd)
+{
+ return create_Channel(JAVAHL_CLASS("/util/RequestChannel"), env, fd);
+}
+jobject create_ResponseChannel(JNIEnv *env, apr_file_t *fd)
+{
+ return create_Channel(JAVAHL_CLASS("/util/ResponseChannel"), env, fd);
+}
+} // anonymous namespace
+
+svn_boolean_t
+OperationContext::checkTunnel(void *tunnel_baton, const char *tunnel_name)
+{
+ JNIEnv *env = JNIUtil::getEnv();
+
+ jstring jtunnel_name = JNIUtil::makeJString(tunnel_name);
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+
+ static jmethodID mid = 0;
+ if (0 == mid)
+ {
+ jclass cls = env->FindClass(JAVAHL_CLASS("/callback/TunnelAgent"));
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+ mid = env->GetMethodID(cls, "checkTunnel",
+ "(Ljava/lang/String;)Z");
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+ }
+
+ jobject jtunnelcb = jobject(tunnel_baton);
+ jboolean check = env->CallBooleanMethod(jtunnelcb, mid, jtunnel_name);
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+
+ return svn_boolean_t(check);
+}
+
+svn_error_t *
+OperationContext::openTunnel(svn_stream_t **request, svn_stream_t **response,
+ svn_ra_close_tunnel_func_t *close_func,
+ void **close_baton,
+ void *tunnel_baton,
+ const char *tunnel_name, const char *user,
+ const char *hostname, int port,
+ svn_cancel_func_t cancel_func, void *cancel_baton,
+ apr_pool_t *pool)
+{
+ TunnelContext *tc = new TunnelContext(pool);
+ if (tc->status)
+ {
+ delete tc;
+ return svn_error_trace(
+ svn_error_wrap_apr(tc->status, _("Could not open tunnel streams")));
+ }
+
+ *close_func = closeTunnel;
+ *close_baton = tc;
+ *request = svn_stream_from_aprfile2(tc->request_out, FALSE, pool);
+ *response = svn_stream_from_aprfile2(tc->response_in, FALSE, pool);
+
+ JNIEnv *env = JNIUtil::getEnv();
+
+ jobject jrequest = create_RequestChannel(env, tc->request_in);
+ SVN_JNI_CATCH(, SVN_ERR_BASE);
+
+ jobject jresponse = create_ResponseChannel(env, tc->response_out);
+ SVN_JNI_CATCH(, SVN_ERR_BASE);
+
+ jstring jtunnel_name = JNIUtil::makeJString(tunnel_name);
+ SVN_JNI_CATCH(, SVN_ERR_BASE);
+
+ jstring juser = JNIUtil::makeJString(user);
+ SVN_JNI_CATCH(, SVN_ERR_BASE);
+
+ jstring jhostname = JNIUtil::makeJString(hostname);
+ SVN_JNI_CATCH(, SVN_ERR_BASE);
+
+ static jmethodID mid = 0;
+ if (0 == mid)
+ {
+ jclass cls = env->FindClass(JAVAHL_CLASS("/callback/TunnelAgent"));
+ SVN_JNI_CATCH(, SVN_ERR_BASE);
+ SVN_JNI_CATCH(
+ mid = env->GetMethodID(
+ cls, "openTunnel",
+ "(Ljava/nio/channels/ReadableByteChannel;"
+ "Ljava/nio/channels/WritableByteChannel;"
+ "Ljava/lang/String;"
+ "Ljava/lang/String;"
+ "Ljava/lang/String;I)"
+ JAVAHL_ARG("/callback/TunnelAgent$CloseTunnelCallback;")),
+ SVN_ERR_BASE);
+ }
+
+ jobject jtunnelcb = jobject(tunnel_baton);
+ SVN_JNI_CATCH(
+ tc->jclosecb = env->CallObjectMethod(
+ jtunnelcb, mid, jrequest, jresponse,
+ jtunnel_name, juser, jhostname, jint(port)),
+ SVN_ERR_BASE);
+
+ return SVN_NO_ERROR;
+}
+
+void
+OperationContext::closeTunnel(void *tunnel_context, void *)
+{
+ TunnelContext* tc = static_cast<TunnelContext*>(tunnel_context);
+ jobject jclosecb = tc->jclosecb;
+ delete tc;
+
+ if (!jclosecb)
+ return;
+
+ JNIEnv *env = JNIUtil::getEnv();
+
+ static jmethodID mid = 0;
+ if (0 == mid)
+ {
+ jclass cls;
+ SVN_JNI_CATCH_VOID(
+ cls= env->FindClass(
+ JAVAHL_CLASS("/callback/TunnelAgent$CloseTunnelCallback")));
+ SVN_JNI_CATCH_VOID(mid = env->GetMethodID(cls, "closeTunnel", "()V"));
+ }
+ SVN_JNI_CATCH_VOID(env->CallVoidMethod(jclosecb, mid));
+}
diff --git a/subversion/bindings/javahl/native/OperationContext.h b/subversion/bindings/javahl/native/OperationContext.h
new file mode 100644
index 0000000..b0d82c2
--- /dev/null
+++ b/subversion/bindings/javahl/native/OperationContext.h
@@ -0,0 +1,128 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file OperationContext.h
+ * @brief Interface of the class OperationContext
+ */
+
+#ifndef JAVAHL_OPERATION_CONTEXT_H
+#define JAVAHL_OPERATION_CONTEXT_H
+
+#include <string>
+#include <memory>
+
+#include "svn_types.h"
+#include "svn_client.h"
+#include "private/svn_atomic.h"
+
+#include <jni.h>
+#include "Pool.h"
+#include "JNIStringHolder.h"
+
+class Prompter;
+
+/**
+ * This class contains a Java objects implementing the interface RaSharedContext
+ */
+class OperationContext
+{
+ private:
+ std::string m_userName;
+ std::string m_passWord;
+ std::string m_configDir;
+
+ apr_hash_t * m_config;
+
+ std::auto_ptr<Prompter> m_prompter;
+ svn_atomic_t m_cancelOperation;
+
+ protected:
+ SVN::Pool *m_pool;
+
+ jobject m_jctx;
+ jobject m_jcfgcb;
+ jobject m_jtunnelcb;
+
+ static void progress(apr_off_t progressVal, apr_off_t total,
+ void *baton, apr_pool_t *pool);
+ void notifyConfigLoad();
+
+ static svn_boolean_t checkTunnel(
+ void *tunnel_baton, const char *tunnel_name);
+
+ static svn_error_t *openTunnel(
+ svn_stream_t **request, svn_stream_t **response,
+ svn_ra_close_tunnel_func_t *close_func, void **close_baton,
+ void *tunnel_baton,
+ const char *tunnel_name, const char *user,
+ const char *hostname, int port,
+ svn_cancel_func_t cancel_func, void *cancel_baton,
+ apr_pool_t *pool);
+
+ static void closeTunnel(
+ void *tunnel_context, void *tunnel_baton);
+
+ public:
+ OperationContext(SVN::Pool &pool);
+ void attachJavaObject(jobject contextHolder, const char *contextClassType, const char *contextFieldName, jfieldID * ctxFieldID);
+ virtual ~OperationContext();
+
+ static svn_error_t *checkCancel(void *cancelBaton);
+
+ virtual void username(const char *pi_username);
+ virtual void password(const char *pi_password);
+ virtual void setPrompt(std::auto_ptr<Prompter> prompter);
+ svn_auth_baton_t *getAuthBaton(SVN::Pool &in_pool);
+
+ void cancelOperation();
+ void resetCancelRequest();
+ virtual bool isCancelledOperation();
+ jobject getSelf() const;
+ const char *getConfigDirectory() const;
+ const char *getUsername() const;
+ const char *getPassword() const;
+ std::auto_ptr<Prompter> clonePrompter() const;
+
+ /**
+ * Set the configuration directory, taking the usual steps to
+ * ensure that Subversion's config file templates exist in the
+ * specified location.
+ */
+ void setConfigDirectory(const char *configDir);
+
+ /**
+ * Return configuration data for the context.
+ * Read it from config directory if necessary
+ */
+ apr_hash_t *getConfigData();
+
+ void setConfigEventHandler(jobject jcfgcb);
+ jobject getConfigEventHandler() const;
+
+ static svn_error_t * clientName(void *baton, const char **name, apr_pool_t *pool);
+ virtual const char * getClientName() const;
+
+ virtual void setTunnelCallback(jobject jtunnelcb);
+ jobject getTunnelCallback() const;
+};
+
+#endif // JAVAHL_OPERATION_CONTEXT_H
diff --git a/subversion/bindings/javahl/native/PatchCallback.cpp b/subversion/bindings/javahl/native/PatchCallback.cpp
index 0cff577..ac1b3d4 100644
--- a/subversion/bindings/javahl/native/PatchCallback.cpp
+++ b/subversion/bindings/javahl/native/PatchCallback.cpp
@@ -80,7 +80,7 @@ PatchCallback::singlePatch(svn_boolean_t *filtered,
static jmethodID mid = 0;
if (mid == 0)
{
- jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/PatchCallback");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/PatchCallback"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN(SVN_NO_ERROR);
@@ -106,7 +106,7 @@ PatchCallback::singlePatch(svn_boolean_t *filtered,
jboolean jfiltered = env->CallBooleanMethod(m_callback, mid, jcanonPath,
jpatchAbsPath, jrejectAbsPath);
if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN(SVN_NO_ERROR);
+ POP_AND_RETURN_EXCEPTION_AS_SVNERROR();
*filtered = (jfiltered ? TRUE : FALSE);
diff --git a/subversion/bindings/javahl/native/PatchCallback.h b/subversion/bindings/javahl/native/PatchCallback.h
index d10a309..61ec883 100644
--- a/subversion/bindings/javahl/native/PatchCallback.h
+++ b/subversion/bindings/javahl/native/PatchCallback.h
@@ -30,8 +30,6 @@
#include <jni.h>
#include "svn_client.h"
-struct info_entry;
-
/**
* This class holds a Java callback object, which will receive every line of
* the file for which the callback information is requested.
diff --git a/subversion/bindings/javahl/native/Path.cpp b/subversion/bindings/javahl/native/Path.cpp
index b9c69ea..c1a1fec 100644
--- a/subversion/bindings/javahl/native/Path.cpp
+++ b/subversion/bindings/javahl/native/Path.cpp
@@ -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::Pool &in_pool)
*
* @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::Pool &in_pool)
* @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::Pool &in_pool)
* @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;
+}
diff --git a/subversion/bindings/javahl/native/Path.h b/subversion/bindings/javahl/native/Path.h
index d23261e..5684efb 100644
--- a/subversion/bindings/javahl/native/Path.h
+++ b/subversion/bindings/javahl/native/Path.h
@@ -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
diff --git a/subversion/bindings/javahl/native/Prompter.cpp b/subversion/bindings/javahl/native/Prompter.cpp
index 14bd312..bad7467 100644
--- a/subversion/bindings/javahl/native/Prompter.cpp
+++ b/subversion/bindings/javahl/native/Prompter.cpp
@@ -25,468 +25,532 @@
*/
#include "Prompter.h"
-#include "Pool.h"
+#include "AuthnCallback.hpp"
+
#include "JNIUtil.h"
#include "JNIStringHolder.h"
#include "../include/org_apache_subversion_javahl_callback_UserPasswordCallback.h"
+
#include <apr_strings.h>
-#include "svn_auth.h"
#include "svn_error.h"
#include "svn_error_codes.h"
#include "svn_private_config.h"
-/**
- * Constructor
- * @param jprompter a global reference to the Java callback object
- */
-Prompter::Prompter(jobject jprompter)
-{
- m_prompter = jprompter;
-}
-Prompter::~Prompter()
+#include "jniwrapper/jni_stack.hpp"
+#include "jniwrapper/jni_string.hpp"
+
+// Class Prompter
+
+Prompter::UniquePtr Prompter::create(jobject jprompter)
{
- if (m_prompter!= NULL)
+ if (!jprompter)
+ return UniquePtr(NULL);
+
+ // Make sure no C++ exceptions are propagated from here.
+ const ::Java::Env jenv;
+ try
{
- // Since the reference to the Java object is a global one, it
- // has to be deleted.
- JNIEnv *env = JNIUtil::getEnv();
- env->DeleteGlobalRef(m_prompter);
+ const jclass cls = ::Java::ClassCache::get_authn_cb(jenv)->get_class();
+ if (!jenv.IsInstanceOf(jprompter, cls))
+ return UniquePtr(NULL);
+
+ return UniquePtr(new Prompter(jenv, jprompter));
}
+ SVN_JAVAHL_JNI_CATCH;
+ return UniquePtr(NULL);
}
-/**
- * Create a C++ peer object for the Java callback object
- *
- * @param jprompter Java callback object
- * @return C++ peer object
- */
-Prompter *Prompter::makeCPrompter(jobject jprompter)
+Prompter::UniquePtr Prompter::clone() const
{
- // If we have no Java object, we need no C++ object.
- if (jprompter == NULL)
- return NULL;
-
- JNIEnv *env = JNIUtil::getEnv();
-
- // Create a local frame for our references
- env->PushLocalFrame(LOCAL_FRAME_SIZE);
- if (JNIUtil::isJavaExceptionThrown())
- return NULL;
+ return create(m_prompter.get());
+}
- // Sanity check that the Java object implements UserPasswordCallback.
- jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/UserPasswordCallback");
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
+Prompter::Prompter(::Java::Env env, jobject jprompter)
+ : m_prompter(env, jprompter)
+{}
- if (!env->IsInstanceOf(jprompter, clazz))
- POP_AND_RETURN_NULL;
+Prompter::~Prompter() {}
- // Create a new global ref for the Java object, because it is
- // longer used that this call.
- jobject myPrompt = env->NewGlobalRef(jprompter);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
- env->PopLocalFrame(NULL);
+svn_auth_provider_object_t *
+Prompter::get_provider_simple(SVN::Pool &in_pool)
+{
+ apr_pool_t *pool = in_pool.getPool();
+ svn_auth_provider_object_t *provider;
+ svn_auth_get_simple_prompt_provider(&provider,
+ simple_prompt,
+ this,
+ 2, /* retry limit */
+ pool);
- // Create the C++ peer.
- return new Prompter(myPrompt);
+ return provider;
}
-/**
- * Retrieve the username from the Java object
- * @return Java string for the username or NULL
- */
-jstring Prompter::username()
+svn_auth_provider_object_t *
+Prompter::get_provider_username(SVN::Pool &in_pool)
{
- JNIEnv *env = JNIUtil::getEnv();
-
- // Create a local frame for our references
- env->PushLocalFrame(LOCAL_FRAME_SIZE);
- if (JNIUtil::isJavaExceptionThrown())
- return NULL;
-
- // The method id will not change during the time this library is
- // loaded, so it can be cached.
- static jmethodID mid = 0;
-
- if (mid == 0)
- {
- jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/UserPasswordCallback");
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
+ apr_pool_t *pool = in_pool.getPool();
+ svn_auth_provider_object_t *provider;
+ svn_auth_get_username_prompt_provider(&provider,
+ username_prompt,
+ this,
+ 2, /* retry limit */
+ pool);
- mid = env->GetMethodID(clazz, "getUsername", "()Ljava/lang/String;");
- if (JNIUtil::isJavaExceptionThrown() || mid == 0)
- POP_AND_RETURN_NULL;
- }
+ return provider;
+}
- jstring ret = static_cast<jstring>(env->CallObjectMethod(m_prompter, mid));
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
+svn_auth_provider_object_t *Prompter::
+get_provider_server_ssl_trust(SVN::Pool &in_pool)
+{
+ apr_pool_t *pool = in_pool.getPool();
+ svn_auth_provider_object_t *provider;
+ svn_auth_get_ssl_server_trust_prompt_provider
+ (&provider, ssl_server_trust_prompt, this, pool);
- return (jstring) env->PopLocalFrame(ret);
+ return provider;
}
-/**
- * Retrieve the password from the Java object
- * @return Java string for the password or NULL
- */
-jstring Prompter::password()
+svn_auth_provider_object_t *Prompter::
+get_provider_client_ssl(SVN::Pool &in_pool)
{
- JNIEnv *env = JNIUtil::getEnv();
+ apr_pool_t *pool = in_pool.getPool();
+ svn_auth_provider_object_t *provider;
+ svn_auth_get_ssl_client_cert_prompt_provider(&provider,
+ ssl_client_cert_prompt,
+ this,
+ 2 /* retry limit */,
+ pool);
- // Create a local frame for our references
- env->PushLocalFrame(LOCAL_FRAME_SIZE);
- if (JNIUtil::isJavaExceptionThrown())
- return NULL;
+ return provider;
+}
- // The method id will not change during the time this library is
- // loaded, so it can be cached.
- static jmethodID mid = 0;
+svn_auth_provider_object_t *
+Prompter::get_provider_client_ssl_password(SVN::Pool &in_pool)
+{
+ apr_pool_t *pool = in_pool.getPool();
+ svn_auth_provider_object_t *provider;
+ svn_auth_get_ssl_client_cert_pw_prompt_provider
+ (&provider, ssl_client_cert_pw_prompt, this, 2 /* retry limit */,
+ pool);
- if (mid == 0)
- {
- jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/UserPasswordCallback");
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
+ return provider;
+}
- mid = env->GetMethodID(clazz, "getPassword", "()Ljava/lang/String;");
- if (JNIUtil::isJavaExceptionThrown() || mid == 0)
- POP_AND_RETURN_NULL;
- }
+svn_error_t *Prompter::simple_prompt(
+ svn_auth_cred_simple_t **cred_p,
+ void *baton,
+ const char *realm,
+ const char *username,
+ svn_boolean_t may_save,
+ apr_pool_t *pool)
+{
+ const ::Java::Env env;
+ svn_error_t *err;
+ SVN_JAVAHL_CATCH(
+ env, SVN_ERR_RA_NOT_AUTHORIZED,
+ err = static_cast<Prompter*>(baton)->dispatch_simple_prompt(
+ env, cred_p, realm, username, may_save, pool));
+ return err;
+}
- jstring ret = static_cast<jstring>(env->CallObjectMethod(m_prompter, mid));
- if (JNIUtil::isJavaExceptionThrown())
- return NULL;
+svn_error_t *Prompter::username_prompt(
+ svn_auth_cred_username_t **cred_p,
+ void *baton,
+ const char *realm,
+ svn_boolean_t may_save,
+ apr_pool_t *pool)
+{
+ const ::Java::Env env;
+ svn_error_t *err;
+ SVN_JAVAHL_CATCH(
+ env, SVN_ERR_RA_NOT_AUTHORIZED,
+ err = static_cast<Prompter*>(baton)->dispatch_username_prompt(
+ env, cred_p, realm, may_save, pool));
+ return err;
+}
- return (jstring) env->PopLocalFrame(ret);
+svn_error_t *Prompter::ssl_server_trust_prompt(
+ svn_auth_cred_ssl_server_trust_t **cred_p,
+ void *baton,
+ const char *realm,
+ apr_uint32_t failures,
+ const svn_auth_ssl_server_cert_info_t *cert_info,
+ svn_boolean_t may_save,
+ apr_pool_t *pool)
+{
+ const ::Java::Env env;
+ svn_error_t *err;
+ SVN_JAVAHL_CATCH(
+ env, SVN_ERR_RA_NOT_AUTHORIZED,
+ err = static_cast<Prompter*>(baton)->dispatch_ssl_server_trust_prompt(
+ env, cred_p, realm, failures, cert_info, may_save, pool));
+ return err;
}
-/**
- * Ask the user a question, which can be answered by yes/no.
- * @param realm the server realm, for which this question is asked
- * @param question the question to ask the user
- * @param yesIsDefault flag if the yes-button should be the default button
- * @return flag who the user answered the question
- */
-bool Prompter::askYesNo(const char *realm, const char *question,
- bool yesIsDefault)
+
+svn_error_t *Prompter::ssl_client_cert_prompt(
+ svn_auth_cred_ssl_client_cert_t **cred_p,
+ void *baton,
+ const char *realm,
+ svn_boolean_t may_save,
+ apr_pool_t *pool)
{
- JNIEnv *env = JNIUtil::getEnv();
+ const ::Java::Env env;
+ svn_error_t *err;
+ SVN_JAVAHL_CATCH(
+ env, SVN_ERR_RA_NOT_AUTHORIZED,
+ err = static_cast<Prompter*>(baton)->dispatch_ssl_client_cert_prompt(
+ env, cred_p, realm, may_save, pool));
+ return err;
+}
- // Create a local frame for our references
- env->PushLocalFrame(LOCAL_FRAME_SIZE);
- if (JNIUtil::isJavaExceptionThrown())
- return false;
+svn_error_t *Prompter::ssl_client_cert_pw_prompt(
+ svn_auth_cred_ssl_client_cert_pw_t **cred_p,
+ void *baton,
+ const char *realm,
+ svn_boolean_t may_save,
+ apr_pool_t *pool)
+{
+ const ::Java::Env env;
+ svn_error_t *err;
+ SVN_JAVAHL_CATCH(
+ env, SVN_ERR_RA_NOT_AUTHORIZED,
+ err = static_cast<Prompter*>(baton)->dispatch_ssl_client_cert_pw_prompt(
+ env, cred_p, realm, may_save, pool));
+ return err;
+}
- // The method id will not change during the time this library is
- // loaded, so it can be cached.
- static jmethodID mid = 0;
+svn_error_t *Prompter::plaintext_prompt(
+ svn_boolean_t *may_save_plaintext,
+ const char *realmstring,
+ void *baton,
+ apr_pool_t *pool)
+{
+ const ::Java::Env env;
+ svn_error_t *err;
+ SVN_JAVAHL_CATCH(
+ env, SVN_ERR_RA_NOT_AUTHORIZED,
+ err = static_cast<Prompter*>(baton)->dispatch_plaintext_prompt(
+ env, may_save_plaintext, realmstring, pool));
+ return err;
+}
- if (mid == 0)
- {
- jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/UserPasswordCallback");
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN(false);
-
- mid = env->GetMethodID(clazz, "askYesNo",
- "(Ljava/lang/String;Ljava/lang/String;Z)Z");
- if (JNIUtil::isJavaExceptionThrown() || mid == 0)
- POP_AND_RETURN(false);
- }
+svn_error_t *Prompter::plaintext_passphrase_prompt(
+ svn_boolean_t *may_save_plaintext,
+ const char *realmstring,
+ void *baton,
+ apr_pool_t *pool)
+{
+ const ::Java::Env env;
+ svn_error_t *err;
+ SVN_JAVAHL_CATCH(
+ env, SVN_ERR_RA_NOT_AUTHORIZED,
+ err = static_cast<Prompter*>(baton)->dispatch_plaintext_passphrase_prompt(
+ env, may_save_plaintext, realmstring, pool));
+ return err;
+}
- // convert the texts to Java strings
- jstring jrealm = JNIUtil::makeJString(realm);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN(false);
- jstring jquestion = JNIUtil::makeJString(question);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN(false);
+svn_error_t *Prompter::dispatch_simple_prompt(
+ ::Java::Env env,
+ svn_auth_cred_simple_t **cred_p,
+ const char *realm,
+ const char *username,
+ svn_boolean_t may_save,
+ apr_pool_t *pool)
+{
+ ::JavaHL::AuthnCallback authn(env, m_prompter.get());
+
+ ::JavaHL::AuthnCallback::AuthnResult result(
+ env,
+ authn.user_password_prompt(::Java::String(env, realm),
+ ::Java::String(env, username),
+ may_save));
+ if (!result.get())
+ return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+ _("User canceled dialog"));
- // execute the callback
- jboolean ret = env->CallBooleanMethod(m_prompter, mid, jrealm, jquestion,
- yesIsDefault ? JNI_TRUE : JNI_FALSE);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN(false);
+ ::Java::String user(env, result.identity());
+ ::Java::String pass(env, result.secret());
+ svn_auth_cred_simple_t *cred =
+ static_cast<svn_auth_cred_simple_t*>(apr_pcalloc(pool, sizeof(*cred)));
+ cred->username = user.strdup(pool);
+ cred->password = pass.strdup(pool);
+ cred->may_save = result.save();
+ *cred_p = cred;
- env->PopLocalFrame(NULL);
- return ret ? true:false;
+ return SVN_NO_ERROR;
}
-const char *Prompter::askQuestion(const char *realm, const char *question,
- bool showAnswer, bool maySave)
+svn_error_t *Prompter::dispatch_username_prompt(
+ ::Java::Env env,
+ svn_auth_cred_username_t **cred_p,
+ const char *realm,
+ svn_boolean_t may_save,
+ apr_pool_t *pool)
{
- JNIEnv *env = JNIUtil::getEnv();
-
- // Create a local frame for our references
- env->PushLocalFrame(LOCAL_FRAME_SIZE);
- if (JNIUtil::isJavaExceptionThrown())
- return NULL;
+ ::JavaHL::AuthnCallback authn(env, m_prompter.get());
- static jmethodID mid = 0;
- static jmethodID mid2 = 0;
- if (mid == 0)
- {
- jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/UserPasswordCallback");
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
-
- mid = env->GetMethodID(clazz, "askQuestion",
- "(Ljava/lang/String;Ljava/lang/String;"
- "ZZ)Ljava/lang/String;");
- if (JNIUtil::isJavaExceptionThrown() || mid == 0)
- POP_AND_RETURN_NULL;
-
- mid2 = env->GetMethodID(clazz, "userAllowedSave", "()Z");
- if (JNIUtil::isJavaExceptionThrown() || mid == 0)
- POP_AND_RETURN_NULL;
- }
+ ::JavaHL::AuthnCallback::AuthnResult result(
+ env,
+ authn.username_prompt(::Java::String(env, realm), may_save));
+ if (!result.get())
+ return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+ _("User canceled dialog"));
- jstring jrealm = JNIUtil::makeJString(realm);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
+ ::Java::String user(env, result.identity());
+ svn_auth_cred_username_t *cred =
+ static_cast<svn_auth_cred_username_t*>(apr_pcalloc(pool, sizeof(*cred)));
+ cred->username = user.strdup(pool);
+ cred->may_save = result.save();
+ *cred_p = cred;
- jstring jquestion = JNIUtil::makeJString(question);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
+ return SVN_NO_ERROR;
+}
- jstring janswer = static_cast<jstring>(
- env->CallObjectMethod(m_prompter, mid, jrealm,
- jquestion,
- showAnswer ? JNI_TRUE : JNI_FALSE,
- maySave ? JNI_TRUE : JNI_FALSE));
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
+svn_error_t *Prompter::dispatch_ssl_server_trust_prompt(
+ ::Java::Env env,
+ svn_auth_cred_ssl_server_trust_t **cred_p,
+ const char *realm,
+ apr_uint32_t failures,
+ const svn_auth_ssl_server_cert_info_t *cert_info,
+ svn_boolean_t may_save,
+ apr_pool_t *pool)
+{
+ ::JavaHL::AuthnCallback authn(env, m_prompter.get());
+
+ ::JavaHL::AuthnCallback::AuthnResult result(
+ env,
+ authn.ssl_server_trust_prompt(
+ ::Java::String(env, realm),
+ ::JavaHL::AuthnCallback::SSLServerCertFailures(env, jint(failures)),
+ ::JavaHL::AuthnCallback::SSLServerCertInfo(env, cert_info->ascii_cert),
+ may_save));
+ if (!result.get())
+ return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+ _("User canceled dialog"));
- JNIStringHolder answer(janswer);
- if (answer != NULL)
- {
- m_answer = answer;
- m_maySave = env->CallBooleanMethod(m_prompter, mid2) ? true: false;
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN_NULL;
- }
- else
+ const bool trust = result.trust();
+ if (!trust)
{
- m_answer = "";
- m_maySave = false;
+ *cred_p = NULL;
+ return SVN_NO_ERROR;
}
- env->PopLocalFrame(NULL);
- return m_answer.c_str();
-}
+ const bool save = result.save();
+ svn_auth_cred_ssl_server_trust_t *cred =
+ static_cast<svn_auth_cred_ssl_server_trust_t*>(apr_pcalloc(pool, sizeof(*cred)));
+ cred->may_save = save;
+ if (save)
+ cred->accepted_failures = failures;
+ *cred_p = cred;
-int Prompter::askTrust(const char *question, bool maySave)
-{
- static jmethodID mid = 0;
- JNIEnv *env = JNIUtil::getEnv();
-
- // Create a local frame for our references
- env->PushLocalFrame(LOCAL_FRAME_SIZE);
- if (JNIUtil::isJavaExceptionThrown())
- return -1;
-
- if (mid == 0)
- {
- jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/UserPasswordCallback");
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN(-1);
-
- mid = env->GetMethodID(clazz, "askTrustSSLServer",
- "(Ljava/lang/String;Z)I");
- if (JNIUtil::isJavaExceptionThrown() || mid == 0)
- POP_AND_RETURN(-1);
- }
- jstring jquestion = JNIUtil::makeJString(question);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN(-1);
-
- jint ret = env->CallIntMethod(m_prompter, mid, jquestion,
- maySave ? JNI_TRUE : JNI_FALSE);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN(-1);
-
- env->PopLocalFrame(NULL);
- return ret;
+ return SVN_NO_ERROR;
}
-bool Prompter::prompt(const char *realm, const char *pi_username, bool maySave)
+svn_error_t *Prompter::dispatch_ssl_client_cert_prompt(
+ ::Java::Env env,
+ svn_auth_cred_ssl_client_cert_t **cred_p,
+ const char *realm,
+ svn_boolean_t may_save,
+ apr_pool_t *pool)
{
- JNIEnv *env = JNIUtil::getEnv();
- jboolean ret;
+ ::JavaHL::AuthnCallback authn(env, m_prompter.get());
- // Create a local frame for our references
- env->PushLocalFrame(LOCAL_FRAME_SIZE);
- if (JNIUtil::isJavaExceptionThrown())
- return false;
+ ::JavaHL::AuthnCallback::AuthnResult result(
+ env,
+ authn.ssl_client_cert_prompt(::Java::String(env, realm), may_save));
+ if (!result.get())
+ return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+ _("User canceled dialog"));
- static jmethodID mid = 0;
- static jmethodID mid2 = 0;
- if (mid == 0)
- {
- jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/UserPasswordCallback");
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN(false);
-
- mid = env->GetMethodID(clazz, "prompt",
- "(Ljava/lang/String;Ljava/lang/String;Z)Z");
- if (JNIUtil::isJavaExceptionThrown() || mid == 0)
- POP_AND_RETURN(false);
-
- mid2 = env->GetMethodID(clazz, "userAllowedSave", "()Z");
- if (JNIUtil::isJavaExceptionThrown() || mid == 0)
- POP_AND_RETURN(false);
- }
+ ::Java::String path(env, result.identity());
+ svn_auth_cred_ssl_client_cert_t *cred =
+ static_cast<svn_auth_cred_ssl_client_cert_t*>(apr_pcalloc(pool, sizeof(*cred)));
+ cred->cert_file = path.strdup(pool);
+ cred->may_save = result.save();
+ *cred_p = cred;
- jstring jrealm = JNIUtil::makeJString(realm);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN(false);
+ return SVN_NO_ERROR;
+}
- jstring jusername = JNIUtil::makeJString(pi_username);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN(false);
+svn_error_t *Prompter::dispatch_ssl_client_cert_pw_prompt(
+ ::Java::Env env,
+ svn_auth_cred_ssl_client_cert_pw_t **cred_p,
+ const char *realm,
+ svn_boolean_t may_save,
+ apr_pool_t *pool)
+{
+ ::JavaHL::AuthnCallback authn(env, m_prompter.get());
- ret = env->CallBooleanMethod(m_prompter, mid, jrealm, jusername,
- maySave ? JNI_TRUE: JNI_FALSE);
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN(false);
+ ::JavaHL::AuthnCallback::AuthnResult result(
+ env,
+ authn.ssl_client_cert_passphrase_prompt(
+ ::Java::String(env, realm), may_save));
+ if (!result.get())
+ return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+ _("User canceled dialog"));
- m_maySave = env->CallBooleanMethod(m_prompter, mid2) ? true : false;
- if (JNIUtil::isJavaExceptionThrown())
- POP_AND_RETURN(false);
+ ::Java::String passphrase(env, result.secret());
+ svn_auth_cred_ssl_client_cert_pw_t *cred =
+ static_cast<svn_auth_cred_ssl_client_cert_pw_t*>(apr_pcalloc(pool, sizeof(*cred)));
+ cred->password = passphrase.strdup(pool);
+ cred->may_save = result.save();
+ *cred_p = cred;
- env->PopLocalFrame(NULL);
- return ret ? true:false;
+ return SVN_NO_ERROR;
}
-svn_auth_provider_object_t *Prompter::getProviderSimple(SVN::Pool &in_pool)
+svn_error_t *Prompter::dispatch_plaintext_prompt(
+ ::Java::Env env,
+ svn_boolean_t *may_save_plaintext,
+ const char *realmstring,
+ apr_pool_t *pool)
{
- apr_pool_t *pool = in_pool.getPool();
- svn_auth_provider_object_t *provider;
- svn_auth_get_simple_prompt_provider(&provider,
- simple_prompt,
- this,
- 2, /* retry limit */
- pool);
-
- return provider;
+ ::JavaHL::AuthnCallback authn(env, m_prompter.get());
+ *may_save_plaintext =
+ authn.allow_store_plaintext_password(::Java::String(env, realmstring));
+ return SVN_NO_ERROR;
}
-svn_auth_provider_object_t *Prompter::getProviderUsername(SVN::Pool &in_pool)
+svn_error_t *Prompter::dispatch_plaintext_passphrase_prompt(
+ ::Java::Env env,
+ svn_boolean_t *may_save_plaintext,
+ const char *realmstring,
+ apr_pool_t *pool)
{
- apr_pool_t *pool = in_pool.getPool();
- svn_auth_provider_object_t *provider;
- svn_auth_get_username_prompt_provider(&provider,
- username_prompt,
- this,
- 2, /* retry limit */
- pool);
-
- return provider;
+ ::JavaHL::AuthnCallback authn(env, m_prompter.get());
+ *may_save_plaintext =
+ authn.allow_store_plaintext_passphrase(::Java::String(env, realmstring));
+ return SVN_NO_ERROR;
}
-svn_auth_provider_object_t *Prompter::getProviderServerSSLTrust(SVN::Pool &in_pool)
+
+// Class CompatPrompter
+
+Prompter::UniquePtr CompatPrompter::create(jobject jprompter)
{
- apr_pool_t *pool = in_pool.getPool();
- svn_auth_provider_object_t *provider;
- svn_auth_get_ssl_server_trust_prompt_provider
- (&provider, ssl_server_trust_prompt, this, pool);
+ if (!jprompter)
+ return UniquePtr(NULL);
- return provider;
+ // Make sure no C++ exceptions are propagated from here.
+ const ::Java::Env jenv;
+ try
+ {
+ const jclass cls =
+ ::Java::ClassCache::get_user_passwd_cb(jenv)->get_class();
+ if (!jenv.IsInstanceOf(jprompter, cls))
+ return UniquePtr(NULL);
+
+ return UniquePtr(new CompatPrompter(jenv, jprompter));
+ }
+ SVN_JAVAHL_JNI_CATCH;
+ return UniquePtr(NULL);
}
-svn_auth_provider_object_t *Prompter::getProviderClientSSL(SVN::Pool &in_pool)
+Prompter::UniquePtr CompatPrompter::clone() const
{
- apr_pool_t *pool = in_pool.getPool();
- svn_auth_provider_object_t *provider;
- svn_auth_get_ssl_client_cert_prompt_provider(&provider,
- ssl_client_cert_prompt,
- this,
- 2 /* retry limit */,
- pool);
-
- return provider;
+ return create(m_prompter.get());
}
-svn_auth_provider_object_t *Prompter::getProviderClientSSLPassword(SVN::Pool &in_pool)
+CompatPrompter::CompatPrompter(::Java::Env env, jobject jprompter)
+ : Prompter(env, jprompter)
+{}
+
+CompatPrompter::~CompatPrompter() {}
+
+namespace {
+jstring compat_ask_question(
+ bool& allowed_save,
+ ::Java::Env env,
+ ::JavaHL::UserPasswordCallback& authn,
+ const char *realm, const char *question,
+ bool show_answer, bool may_save)
{
- apr_pool_t *pool = in_pool.getPool();
- svn_auth_provider_object_t *provider;
- svn_auth_get_ssl_client_cert_pw_prompt_provider
- (&provider, ssl_client_cert_pw_prompt, this, 2 /* retry limit */,
- pool);
+ const jstring janswer =
+ authn.ask_question(::Java::String(env, realm),
+ ::Java::String(env, question),
+ show_answer, may_save);
- return provider;
-}
+ if (janswer)
+ allowed_save = authn.user_allowed_save();
+ else
+ allowed_save = false;
-svn_error_t *Prompter::simple_prompt(svn_auth_cred_simple_t **cred_p,
- void *baton,
- const char *realm, const char *username,
- svn_boolean_t may_save,
- apr_pool_t *pool)
+ return janswer;
+}
+} // anonymous namespace
+
+svn_error_t *CompatPrompter::dispatch_simple_prompt(
+ ::Java::Env env,
+ svn_auth_cred_simple_t **cred_p,
+ const char *realm, const char *username,
+ svn_boolean_t may_save,
+ apr_pool_t *pool)
{
- Prompter *that = static_cast<Prompter *>(baton);
- svn_auth_cred_simple_t *ret =
- reinterpret_cast<svn_auth_cred_simple_t*>(apr_pcalloc(pool, sizeof(*ret)));
- if (!that->prompt(realm, username, may_save ? true : false))
- return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
- _("User canceled dialog"));
- jstring juser = that->username();
- JNIStringHolder user(juser);
- if (user == NULL)
+ ::JavaHL::UserPasswordCallback authn(env, m_prompter.get());
+
+ if (!authn.prompt(::Java::String(env, realm),
+ ::Java::String(env, username),
+ may_save))
return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
_("User canceled dialog"));
- ret->username = apr_pstrdup(pool,user);
- jstring jpass = that->password();
- JNIStringHolder pass(jpass);
- if (pass == NULL)
+ const ::Java::String user(env, authn.get_username());
+ const ::Java::String pass(env, authn.get_password());
+
+ if (!user.get() || !pass.get())
return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
_("User canceled dialog"));
- else
- {
- ret->password = apr_pstrdup(pool, pass);
- ret->may_save = that->m_maySave;
- }
- *cred_p = ret;
+
+ svn_auth_cred_simple_t *cred =
+ static_cast<svn_auth_cred_simple_t*>(apr_pcalloc(pool, sizeof(*cred)));
+ cred->username = user.strdup(pool);
+ cred->password = pass.strdup(pool);
+ cred->may_save = authn.user_allowed_save();
+ *cred_p = cred;
return SVN_NO_ERROR;
}
-svn_error_t *Prompter::username_prompt(svn_auth_cred_username_t **cred_p,
- void *baton,
- const char *realm,
- svn_boolean_t may_save,
- apr_pool_t *pool)
+svn_error_t *CompatPrompter::dispatch_username_prompt(
+ ::Java::Env env,
+ svn_auth_cred_username_t **cred_p,
+ const char *realm,
+ svn_boolean_t may_save,
+ apr_pool_t *pool)
{
- Prompter *that = static_cast<Prompter *>(baton);
- svn_auth_cred_username_t *ret =
- reinterpret_cast<svn_auth_cred_username_t*>(apr_pcalloc(pool, sizeof(*ret)));
- const char *user = that->askQuestion(realm, _("Username: "), true,
- may_save ? true : false);
- if (user == NULL)
+ ::JavaHL::UserPasswordCallback authn(env, m_prompter.get());
+
+ bool allowed_save;
+ const ::Java::String user(
+ env,
+ compat_ask_question(allowed_save, env, authn,
+ realm, _("Username: "), true, may_save));
+ if (!user.get())
return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
_("User canceled dialog"));
- ret->username = apr_pstrdup(pool,user);
- ret->may_save = that->m_maySave;
- *cred_p = ret;
+
+ svn_auth_cred_username_t *cred =
+ static_cast<svn_auth_cred_username_t*>(apr_pcalloc(pool, sizeof(*cred)));
+ cred->username = user.strdup(pool);
+ cred->may_save = allowed_save;
+ *cred_p = cred;
return SVN_NO_ERROR;
}
svn_error_t *
-Prompter::ssl_server_trust_prompt(svn_auth_cred_ssl_server_trust_t **cred_p,
- void *baton,
- const char *realm,
- apr_uint32_t failures,
- const svn_auth_ssl_server_cert_info_t *cert_info,
- svn_boolean_t may_save,
- apr_pool_t *pool)
+CompatPrompter::dispatch_ssl_server_trust_prompt(
+ ::Java::Env env,
+ svn_auth_cred_ssl_server_trust_t **cred_p,
+ const char *realm,
+ apr_uint32_t failures,
+ const svn_auth_ssl_server_cert_info_t *cert_info,
+ svn_boolean_t may_save,
+ apr_pool_t *pool)
{
- Prompter *that = static_cast<Prompter *>(baton);
- svn_auth_cred_ssl_server_trust_t *ret =
- reinterpret_cast<svn_auth_cred_ssl_server_trust_t*>(apr_pcalloc(pool, sizeof(*ret)));
+ ::JavaHL::UserPasswordCallback authn(env, m_prompter.get());
std::string question = _("Error validating server certificate for ");
question += realm;
@@ -526,16 +590,20 @@ Prompter::ssl_server_trust_prompt(svn_auth_cred_ssl_server_trust_t **cred_p,
question += "\n";
}
- switch(that->askTrust(question.c_str(), may_save ? true : false))
+ svn_auth_cred_ssl_server_trust_t *cred =
+ static_cast<svn_auth_cred_ssl_server_trust_t*>(apr_pcalloc(pool, sizeof(*cred)));
+
+ switch (authn.ask_trust_ssl_server(::Java::String(env, question), may_save))
{
case org_apache_subversion_javahl_callback_UserPasswordCallback_AcceptTemporary:
- *cred_p = ret;
- ret->may_save = FALSE;
+ cred->may_save = FALSE;
+ cred->accepted_failures = failures;
+ *cred_p = cred;
break;
case org_apache_subversion_javahl_callback_UserPasswordCallback_AcceptPermanently:
- *cred_p = ret;
- ret->may_save = TRUE;
- ret->accepted_failures = failures;
+ cred->may_save = TRUE;
+ cred->accepted_failures = failures;
+ *cred_p = cred;
break;
default:
*cred_p = NULL;
@@ -544,79 +612,91 @@ Prompter::ssl_server_trust_prompt(svn_auth_cred_ssl_server_trust_t **cred_p,
}
svn_error_t *
-Prompter::ssl_client_cert_prompt(svn_auth_cred_ssl_client_cert_t **cred_p,
- void *baton,
- const char *realm,
- svn_boolean_t may_save,
- apr_pool_t *pool)
+CompatPrompter::dispatch_ssl_client_cert_prompt(
+ ::Java::Env env,
+ svn_auth_cred_ssl_client_cert_t **cred_p,
+ const char *realm,
+ svn_boolean_t may_save,
+ apr_pool_t *pool)
{
- Prompter *that = static_cast<Prompter *>(baton);
- svn_auth_cred_ssl_client_cert_t *ret =
- reinterpret_cast<svn_auth_cred_ssl_client_cert_t*>(apr_pcalloc(pool, sizeof(*ret)));
- const char *cert_file =
- that->askQuestion(realm, _("client certificate filename: "), true,
- may_save ? true : false);
- if (cert_file == NULL)
+ ::JavaHL::UserPasswordCallback authn(env, m_prompter.get());
+
+ bool allowed_save;
+ const ::Java::String path(
+ env,
+ compat_ask_question(allowed_save, env, authn, realm,
+ _("Client certificate filename: "),
+ true, may_save));
+ if (!path.get())
return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
_("User canceled dialog"));
- ret->cert_file = apr_pstrdup(pool, cert_file);
- ret->may_save = that->m_maySave;
- *cred_p = ret;
+
+ svn_auth_cred_ssl_client_cert_t *cred =
+ static_cast<svn_auth_cred_ssl_client_cert_t*>(apr_pcalloc(pool, sizeof(*cred)));
+ cred->cert_file = path.strdup(pool);
+ cred->may_save = allowed_save;
+ *cred_p = cred;
return SVN_NO_ERROR;
}
svn_error_t *
-Prompter::ssl_client_cert_pw_prompt(svn_auth_cred_ssl_client_cert_pw_t **cred_p,
- void *baton,
- const char *realm,
- svn_boolean_t may_save,
- apr_pool_t *pool)
+CompatPrompter::dispatch_ssl_client_cert_pw_prompt(
+ ::Java::Env env,
+ svn_auth_cred_ssl_client_cert_pw_t **cred_p,
+ const char *realm,
+ svn_boolean_t may_save,
+ apr_pool_t *pool)
{
- Prompter *that = static_cast<Prompter *>(baton);
- svn_auth_cred_ssl_client_cert_pw_t *ret =
- reinterpret_cast<svn_auth_cred_ssl_client_cert_pw_t*>(apr_pcalloc(pool, sizeof(*ret)));
- const char *info = that->askQuestion(realm,
- _("client certificate passphrase: "),
- false, may_save ? true : false);
- if (info == NULL)
+ ::JavaHL::UserPasswordCallback authn(env, m_prompter.get());
+
+ bool allowed_save;
+ const ::Java::String info(
+ env,
+ compat_ask_question(allowed_save, env, authn, realm,
+ _("Client certificate passphrase: "),
+ false, may_save));
+ if (!info.get())
return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
_("User canceled dialog"));
- ret->password = apr_pstrdup(pool, info);
- ret->may_save = that->m_maySave;
- *cred_p = ret;
+
+ svn_auth_cred_ssl_client_cert_pw_t *cred =
+ static_cast<svn_auth_cred_ssl_client_cert_pw_t*>(apr_pcalloc(pool, sizeof(*cred)));
+ cred->password = info.strdup(pool);
+ cred->may_save = allowed_save;
+ *cred_p = cred;
return SVN_NO_ERROR;
}
svn_error_t *
-Prompter::plaintext_prompt(svn_boolean_t *may_save_plaintext,
- const char *realmstring,
- void *baton,
- apr_pool_t *pool)
+CompatPrompter::dispatch_plaintext_prompt(
+ ::Java::Env env,
+ svn_boolean_t *may_save_plaintext,
+ const char *realmstring,
+ apr_pool_t *pool)
{
- Prompter *that = static_cast<Prompter *>(baton);
+ ::JavaHL::UserPasswordCallback authn(env, m_prompter.get());
- bool result = that->askYesNo(realmstring,
- _("Store password unencrypted?"),
- false);
-
- *may_save_plaintext = (result ? TRUE : FALSE);
+ *may_save_plaintext = authn.ask_yes_no(
+ ::Java::String(env, realmstring),
+ ::Java::String(env, _("Store password unencrypted?")),
+ false);
return SVN_NO_ERROR;
}
svn_error_t *
-Prompter::plaintext_passphrase_prompt(svn_boolean_t *may_save_plaintext,
- const char *realmstring,
- void *baton,
- apr_pool_t *pool)
+CompatPrompter::dispatch_plaintext_passphrase_prompt(
+ ::Java::Env env,
+ svn_boolean_t *may_save_plaintext,
+ const char *realmstring,
+ apr_pool_t *pool)
{
- Prompter *that = static_cast<Prompter *>(baton);
-
- bool result = that->askYesNo(realmstring,
- _("Store passphrase unencrypted?"),
- false);
+ ::JavaHL::UserPasswordCallback authn(env, m_prompter.get());
- *may_save_plaintext = (result ? TRUE : FALSE);
+ *may_save_plaintext = authn.ask_yes_no(
+ ::Java::String(env, realmstring),
+ ::Java::String(env, _("Store passphrase unencrypted?")),
+ false);
return SVN_NO_ERROR;
}
diff --git a/subversion/bindings/javahl/native/Prompter.h b/subversion/bindings/javahl/native/Prompter.h
index 722abce..06a7f2d 100644
--- a/subversion/bindings/javahl/native/Prompter.h
+++ b/subversion/bindings/javahl/native/Prompter.h
@@ -24,93 +24,219 @@
* @brief Interface of the class Prompter
*/
-#ifndef PROMPTER_H
-#define PROMPTER_H
+#ifndef SVN_JAVAHL_PROMPTER_H
+#define SVN_JAVAHL_PROMPTER_H
+
+#include <memory>
-#include <jni.h>
#include "svn_auth.h"
-#include <string>
+
#include "Pool.h"
-/**
- * This class requests username/password and informations about
- * ssl-certificates from the user.
- */
+
+#include "jniwrapper/jni_globalref.hpp"
+
class Prompter
{
- private:
+public:
+ typedef ::std::auto_ptr<Prompter> UniquePtr;
+
/**
- * The Java callback object.
+ * Factory method; @a prompter is a local reference to the Java
+ * callback object.
*/
- jobject m_prompter;
+ static Prompter::UniquePtr create(jobject jprompter);
/**
- * Tntermediate storage for an answer.
+ * Return a clone of the current object, referring to the same Java
+ * prompter object.
*/
- std::string m_answer;
+ virtual Prompter::UniquePtr clone() const;
+
+ virtual ~Prompter();
+
+ svn_auth_provider_object_t *get_provider_username(SVN::Pool &in_pool);
+ svn_auth_provider_object_t *get_provider_simple(SVN::Pool &in_pool);
+ svn_auth_provider_object_t *get_provider_server_ssl_trust(SVN::Pool &in_pool);
+ svn_auth_provider_object_t *get_provider_client_ssl(SVN::Pool &in_pool);
+ svn_auth_provider_object_t *get_provider_client_ssl_password(SVN::Pool &in_pool);
+
+protected:
+ explicit Prompter(::Java::Env env, jobject jprompter);
/**
- * Flag is the user allowed, that the last answer is stored in the
- * configuration.
+ * The Java callback object.
*/
- bool m_maySave;
-
- Prompter(jobject jprompter);
- bool prompt(const char *realm, const char *pi_username, bool maySave);
- bool askYesNo(const char *realm, const char *question, bool yesIsDefault);
- const char *askQuestion(const char *realm, const char *question,
- bool showAnswer, bool maySave);
- int askTrust(const char *question, bool maySave);
- jstring password();
- jstring username();
- static svn_error_t *simple_prompt(svn_auth_cred_simple_t **cred_p,
- void *baton, const char *realm,
- const char *username,
- svn_boolean_t may_save,
- apr_pool_t *pool);
- static svn_error_t *username_prompt
- (svn_auth_cred_username_t **cred_p,
- void *baton,
- const char *realm,
- svn_boolean_t may_save,
- apr_pool_t *pool);
- static svn_error_t *ssl_server_trust_prompt
- (svn_auth_cred_ssl_server_trust_t **cred_p,
- void *baton,
- const char *realm,
- apr_uint32_t failures,
- const svn_auth_ssl_server_cert_info_t *cert_info,
- svn_boolean_t may_save,
- apr_pool_t *pool);
- static svn_error_t *ssl_client_cert_prompt
- (svn_auth_cred_ssl_client_cert_t **cred_p,
- void *baton,
- const char *realm,
- svn_boolean_t may_save,
- apr_pool_t *pool);
- static svn_error_t *ssl_client_cert_pw_prompt
- (svn_auth_cred_ssl_client_cert_pw_t **cred_p,
- void *baton,
- const char *realm,
- svn_boolean_t may_save,
- apr_pool_t *pool);
- public:
- static Prompter *makeCPrompter(jobject jprompter);
- ~Prompter();
- svn_auth_provider_object_t *getProviderUsername(SVN::Pool &in_pool);
- svn_auth_provider_object_t *getProviderSimple(SVN::Pool &in_pool);
- svn_auth_provider_object_t *getProviderServerSSLTrust(SVN::Pool &in_pool);
- svn_auth_provider_object_t *getProviderClientSSL(SVN::Pool &in_pool);
- svn_auth_provider_object_t *getProviderClientSSLPassword(SVN::Pool &in_pool);
-
- static svn_error_t *plaintext_prompt(svn_boolean_t *may_save_plaintext,
- const char *realmstring,
- void *baton,
- apr_pool_t *pool);
+ ::Java::GlobalObject m_prompter;
+
+ virtual svn_error_t *dispatch_simple_prompt(
+ ::Java::Env env,
+ svn_auth_cred_simple_t **cred_p,
+ const char *realm,
+ const char *username,
+ svn_boolean_t may_save,
+ apr_pool_t *pool);
+
+ static svn_error_t *simple_prompt(
+ svn_auth_cred_simple_t **cred_p,
+ void *baton,
+ const char *realm,
+ const char *username,
+ svn_boolean_t may_save,
+ apr_pool_t *pool);
+
+ virtual svn_error_t *dispatch_username_prompt(
+ ::Java::Env env,
+ svn_auth_cred_username_t **cred_p,
+ const char *realm,
+ svn_boolean_t may_save,
+ apr_pool_t *pool);
+
+ static svn_error_t *username_prompt(
+ svn_auth_cred_username_t **cred_p,
+ void *baton,
+ const char *realm,
+ svn_boolean_t may_save,
+ apr_pool_t *pool);
+
+ virtual svn_error_t *dispatch_ssl_server_trust_prompt(
+ ::Java::Env env,
+ svn_auth_cred_ssl_server_trust_t **cred_p,
+ const char *realm,
+ apr_uint32_t failures,
+ const svn_auth_ssl_server_cert_info_t *cert_info,
+ svn_boolean_t may_save,
+ apr_pool_t *pool);
+
+ static svn_error_t *ssl_server_trust_prompt(
+ svn_auth_cred_ssl_server_trust_t **cred_p,
+ void *baton,
+ const char *realm,
+ apr_uint32_t failures,
+ const svn_auth_ssl_server_cert_info_t *cert_info,
+ svn_boolean_t may_save,
+ apr_pool_t *pool);
+
+ virtual svn_error_t *dispatch_ssl_client_cert_prompt(
+ ::Java::Env env,
+ svn_auth_cred_ssl_client_cert_t **cred_p,
+ const char *realm,
+ svn_boolean_t may_save,
+ apr_pool_t *pool);
+
+ static svn_error_t *ssl_client_cert_prompt(
+ svn_auth_cred_ssl_client_cert_t **cred_p,
+ void *baton,
+ const char *realm,
+ svn_boolean_t may_save,
+ apr_pool_t *pool);
+
+ virtual svn_error_t *dispatch_ssl_client_cert_pw_prompt(
+ ::Java::Env env,
+ svn_auth_cred_ssl_client_cert_pw_t **cred_p,
+ const char *realm,
+ svn_boolean_t may_save,
+ apr_pool_t *pool);
+
+ static svn_error_t *ssl_client_cert_pw_prompt(
+ svn_auth_cred_ssl_client_cert_pw_t **cred_p,
+ void *baton,
+ const char *realm,
+ svn_boolean_t may_save,
+ apr_pool_t *pool);
+
+protected:
+ virtual svn_error_t *dispatch_plaintext_prompt(
+ ::Java::Env env,
+ svn_boolean_t *may_save_plaintext,
+ const char *realmstring,
+ apr_pool_t *pool);
+
+public:
+ static svn_error_t *plaintext_prompt(
+ svn_boolean_t *may_save_plaintext,
+ const char *realmstring,
+ void *baton,
+ apr_pool_t *pool);
+
+protected:
+ virtual svn_error_t *dispatch_plaintext_passphrase_prompt(
+ ::Java::Env env,
+ svn_boolean_t *may_save_plaintext,
+ const char *realmstring,
+ apr_pool_t *pool);
+
+public:
static svn_error_t *plaintext_passphrase_prompt(
- svn_boolean_t *may_save_plaintext,
- const char *realmstring,
- void *baton,
- apr_pool_t *pool);
+ svn_boolean_t *may_save_plaintext,
+ const char *realmstring,
+ void *baton,
+ apr_pool_t *pool);
+};
+
+
+/**
+ * This class requests username/password and informations about
+ * ssl-certificates from the user.
+ */
+class CompatPrompter : public Prompter
+{
+public:
+ static Prompter::UniquePtr create(jobject jprompter);
+ virtual Prompter::UniquePtr clone() const;
+ virtual ~CompatPrompter();
+
+protected:
+ explicit CompatPrompter(::Java::Env env, jobject jprompter);
+
+ virtual svn_error_t *dispatch_simple_prompt(
+ ::Java::Env env,
+ svn_auth_cred_simple_t **cred_p,
+ const char *realm,
+ const char *username,
+ svn_boolean_t may_save,
+ apr_pool_t *pool);
+
+ virtual svn_error_t *dispatch_username_prompt(
+ ::Java::Env env,
+ svn_auth_cred_username_t **cred_p,
+ const char *realm,
+ svn_boolean_t may_save,
+ apr_pool_t *pool);
+
+ virtual svn_error_t *dispatch_ssl_server_trust_prompt(
+ ::Java::Env env,
+ svn_auth_cred_ssl_server_trust_t **cred_p,
+ const char *realm,
+ apr_uint32_t failures,
+ const svn_auth_ssl_server_cert_info_t *cert_info,
+ svn_boolean_t may_save,
+ apr_pool_t *pool);
+
+ virtual svn_error_t *dispatch_ssl_client_cert_prompt(
+ ::Java::Env env,
+ svn_auth_cred_ssl_client_cert_t **cred_p,
+ const char *realm,
+ svn_boolean_t may_save,
+ apr_pool_t *pool);
+
+ virtual svn_error_t *dispatch_ssl_client_cert_pw_prompt(
+ ::Java::Env env,
+ svn_auth_cred_ssl_client_cert_pw_t **cred_p,
+ const char *realm,
+ svn_boolean_t may_save,
+ apr_pool_t *pool);
+
+ virtual svn_error_t *dispatch_plaintext_prompt(
+ ::Java::Env env,
+ svn_boolean_t *may_save_plaintext,
+ const char *realmstring,
+ apr_pool_t *pool);
+
+ virtual svn_error_t *dispatch_plaintext_passphrase_prompt(
+ ::Java::Env env,
+ svn_boolean_t *may_save_plaintext,
+ const char *realmstring,
+ apr_pool_t *pool);
};
-#endif // PROMPTER_H
+#endif // SVN_JAVAHL_PROMPTER_H
diff --git a/subversion/bindings/javahl/native/RevpropTable.cpp b/subversion/bindings/javahl/native/PropertyTable.cpp
index 5da74dd..0a44f0c 100644
--- a/subversion/bindings/javahl/native/RevpropTable.cpp
+++ b/subversion/bindings/javahl/native/PropertyTable.cpp
@@ -20,13 +20,14 @@
* ====================================================================
* @endcopyright
*
- * @file RevpropTable.cpp
- * @brief Implementation of the class RevpropTable
+ * @file PropertyTable.cpp
+ * @brief Implementation of the class PropertyTable
*/
-#include "RevpropTable.h"
+#include "PropertyTable.h"
#include "JNIUtil.h"
#include "JNIStringHolder.h"
+#include "JNIByteArray.h"
#include "Array.h"
#include <apr_tables.h>
#include <apr_strings.h>
@@ -35,15 +36,15 @@
#include "svn_props.h"
#include <iostream>
-RevpropTable::~RevpropTable()
+PropertyTable::~PropertyTable()
{
if (m_revpropTable != NULL)
JNIUtil::getEnv()->DeleteLocalRef(m_revpropTable);
}
-const apr_hash_t *RevpropTable::hash(const SVN::Pool &pool)
+apr_hash_t *PropertyTable::hash(const SVN::Pool &pool)
{
- if (m_revprops.size() == 0)
+ if (!m_revpropTable && !m_empty_if_null)
return NULL;
apr_hash_t *revprop_table = apr_hash_make(pool.getPool());
@@ -57,13 +58,14 @@ const apr_hash_t *RevpropTable::hash(const SVN::Pool &pool)
const char *msg = apr_psprintf(pool.getPool(),
"Invalid property name: '%s'",
propname);
- JNIUtil::throwNativeException(JAVA_PACKAGE "/ClientException", msg,
+ JNIUtil::throwNativeException(JAVAHL_CLASS("/ClientException"), msg,
NULL, SVN_ERR_CLIENT_PROPERTY_NAME);
return NULL;
}
- svn_string_t *propval = svn_string_create(it->second.c_str(),
- pool.getPool());
+ svn_string_t *propval = svn_string_ncreate(it->second.c_str(),
+ it->second.size(),
+ pool.getPool());
apr_hash_set(revprop_table, propname, APR_HASH_KEY_STRING, propval);
}
@@ -71,7 +73,10 @@ const apr_hash_t *RevpropTable::hash(const SVN::Pool &pool)
return revprop_table;
}
-RevpropTable::RevpropTable(jobject jrevpropTable)
+PropertyTable::PropertyTable(jobject jrevpropTable, bool bytearray_values,
+ bool empty_if_null)
+ : m_revpropTable(jrevpropTable),
+ m_empty_if_null(empty_if_null)
{
m_revpropTable = jrevpropTable;
@@ -116,12 +121,27 @@ RevpropTable::RevpropTable(jobject jrevpropTable)
if (JNIUtil::isExceptionThrown())
return;
- JNIStringHolder propval((jstring)jpropval);
- if (JNIUtil::isExceptionThrown())
- return;
-
- m_revprops[std::string(static_cast<const char *>(propname))]
- = std::string(static_cast<const char *>(propval));
+ std::string pv;
+ if (bytearray_values)
+ {
+ JNIByteArray propval((jbyteArray)jpropval);
+ if (JNIUtil::isExceptionThrown())
+ return;
+ if (!propval.isNull())
+ pv = std::string(
+ reinterpret_cast<const char*>(propval.getBytes()),
+ propval.getLength());
+ }
+ else
+ {
+ JNIStringHolder propval((jstring)jpropval);
+ if (JNIUtil::isExceptionThrown())
+ return;
+ if (NULL != static_cast<const char *>(propval))
+ pv = static_cast<const char *>(propval);
+ }
+
+ m_revprops[std::string(static_cast<const char *>(propname))] = pv;
JNIUtil::getEnv()->DeleteLocalRef(jpropval);
}
diff --git a/subversion/bindings/javahl/native/RevpropTable.h b/subversion/bindings/javahl/native/PropertyTable.h
index ee5f14f..8a9a335 100644
--- a/subversion/bindings/javahl/native/RevpropTable.h
+++ b/subversion/bindings/javahl/native/PropertyTable.h
@@ -20,8 +20,8 @@
* ====================================================================
* @endcopyright
*
- * @file RevpropTable.h
- * @brief Interface of the class RevpropTable
+ * @file PropertyTable.h
+ * @brief Interface of the class PropertyTable
*/
#ifndef REVPROPTABLE_H
@@ -36,15 +36,18 @@ struct apr_hash_t;
#include <map>
#include <string>
-class RevpropTable
+class PropertyTable
{
private:
std::map<std::string, std::string> m_revprops;
jobject m_revpropTable;
+ bool m_empty_if_null;
public:
- RevpropTable(jobject jrevpropTable);
- ~RevpropTable();
- const apr_hash_t *hash(const SVN::Pool &pool);
+ PropertyTable(jobject jrevpropTable,
+ bool bytearray_values,
+ bool empty_if_null);
+ ~PropertyTable();
+ apr_hash_t *hash(const SVN::Pool &pool);
};
#endif // REVPROPTABLE_H
diff --git a/subversion/bindings/javahl/native/ProplistCallback.cpp b/subversion/bindings/javahl/native/ProplistCallback.cpp
index be46cf5..85d28c5 100644
--- a/subversion/bindings/javahl/native/ProplistCallback.cpp
+++ b/subversion/bindings/javahl/native/ProplistCallback.cpp
@@ -89,7 +89,7 @@ svn_error_t *ProplistCallback::singlePath(const char *path,
static volatile jmethodID mid = 0;
if (mid == 0)
{
- jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/ProplistCallback");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/ProplistCallback"));
if (JNIUtil::isJavaExceptionThrown())
return SVN_NO_ERROR;
@@ -104,7 +104,7 @@ svn_error_t *ProplistCallback::singlePath(const char *path,
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN(SVN_NO_ERROR);
- jobject jmap = CreateJ::PropertyMap(prop_hash);
+ jobject jmap = CreateJ::PropertyMap(prop_hash, pool);
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN(SVN_NO_ERROR);
@@ -112,9 +112,7 @@ svn_error_t *ProplistCallback::singlePath(const char *path,
env->CallVoidMethod(m_callback, mid, jpath, jmap);
// We return whether an exception was thrown or not.
- env->PopLocalFrame(NULL);
-
- return SVN_NO_ERROR;
+ POP_AND_RETURN_EXCEPTION_AS_SVNERROR();
}
@@ -144,7 +142,7 @@ svn_error_t *ProplistCallback::singlePath(
static jmethodID mid = 0;
if (mid == 0)
{
- jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/InheritedProplistCallback");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/InheritedProplistCallback"));
if (JNIUtil::isJavaExceptionThrown())
return SVN_NO_ERROR;
@@ -159,7 +157,7 @@ svn_error_t *ProplistCallback::singlePath(
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN(SVN_NO_ERROR);
- jobject jmap = CreateJ::PropertyMap(prop_hash);
+ jobject jmap = CreateJ::PropertyMap(prop_hash, pool);
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN(SVN_NO_ERROR);
diff --git a/subversion/bindings/javahl/native/RemoteSession.cpp b/subversion/bindings/javahl/native/RemoteSession.cpp
new file mode 100644
index 0000000..42bfe75
--- /dev/null
+++ b/subversion/bindings/javahl/native/RemoteSession.cpp
@@ -0,0 +1,1322 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file RemoteSession.cpp
+ * @brief Implementation of the class RemoteSession
+ */
+
+#include <cstring>
+#include <memory>
+#include <set>
+
+#include "JNIByteArray.h"
+#include "JNIStringHolder.h"
+#include "JNIUtil.h"
+#include "Path.h"
+
+#include "svn_ra.h"
+#include "svn_string.h"
+#include "svn_dirent_uri.h"
+#include "svn_delta.h"
+
+#include "CreateJ.h"
+#include "EnumMapper.h"
+#include "Iterator.h"
+#include "LogMessageCallback.h"
+#include "OutputStream.h"
+#include "Prompter.h"
+#include "Revision.h"
+#include "RemoteSession.h"
+#include "EditorProxy.h"
+#include "StateReporter.h"
+
+#include <apr_strings.h>
+#include "svn_private_config.h"
+
+#define JAVA_CLASS_REMOTE_SESSION JAVAHL_CLASS("/remote/RemoteSession")
+
+RemoteSession *
+RemoteSession::getCppObject(jobject jthis)
+{
+ static jfieldID fid = 0;
+ jlong cppAddr = SVNBase::findCppAddrForJObject(jthis, &fid,
+ JAVA_CLASS_REMOTE_SESSION);
+ return (cppAddr == 0 ? NULL : reinterpret_cast<RemoteSession *>(cppAddr));
+}
+
+jobject
+RemoteSession::open(jint jretryAttempts,
+ jstring jurl, jstring juuid,
+ jstring jconfigDirectory,
+ jstring jusername, jstring jpassword,
+ jobject jprompter, jobject jdeprecatedPrompter,
+ jobject jprogress, jobject jcfgcb, jobject jtunnelcb)
+{
+ SVN_ERR_ASSERT_NO_RETURN(!(jprompter && jdeprecatedPrompter));
+
+ SVN::Pool requestPool;
+ URL url(jurl, requestPool);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ SVN_JNI_ERR(url.error_occurred(), NULL);
+
+ JNIStringHolder uuid(juuid);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+
+ Path configDirectory(jconfigDirectory, requestPool);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ SVN_JNI_ERR(configDirectory.error_occurred(), NULL);
+
+ JNIStringHolder usernameStr(jusername);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+
+ JNIStringHolder passwordStr(jpassword);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+
+ Prompter::UniquePtr prompter(jprompter ? Prompter::create(jprompter)
+ : CompatPrompter::create(jdeprecatedPrompter));
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+
+ jobject jremoteSession = open(
+ jretryAttempts, url.c_str(), uuid,
+ (jconfigDirectory ? configDirectory.c_str() : NULL),
+ usernameStr, passwordStr, prompter, jprogress, jcfgcb, jtunnelcb);
+ if (JNIUtil::isExceptionThrown() || !jremoteSession)
+ jremoteSession = NULL;
+ return jremoteSession;
+}
+
+jobject
+RemoteSession::open(jint jretryAttempts,
+ const char* url, const char* uuid,
+ const char* configDirectory,
+ const char* usernameStr, const char* passwordStr,
+ Prompter::UniquePtr prompter, jobject jprogress,
+ jobject jcfgcb, jobject jtunnelcb)
+{
+ RemoteSession* session = new RemoteSession(
+ jretryAttempts, url, uuid, configDirectory,
+ usernameStr, passwordStr, prompter, jcfgcb, jtunnelcb);
+ if (JNIUtil::isJavaExceptionThrown() || !session)
+ {
+ delete session;
+ return NULL;
+ }
+
+ // Create java session object
+ JNIEnv *env = JNIUtil::getEnv();
+
+ jclass clazz = env->FindClass(JAVA_CLASS_REMOTE_SESSION);
+ if (JNIUtil::isJavaExceptionThrown())
+ {
+ delete session;
+ return NULL;
+ }
+
+ static jmethodID ctor = 0;
+ if (ctor == 0)
+ {
+ ctor = env->GetMethodID(clazz, "<init>", "(J)V");
+ if (JNIUtil::isJavaExceptionThrown())
+ {
+ delete session;
+ return NULL;
+ }
+ }
+
+ jobject jremoteSession = env->NewObject(clazz, ctor, session->getCppAddr());
+ if (JNIUtil::isJavaExceptionThrown())
+ {
+ delete session;
+ return NULL;
+ }
+
+ session->m_context->activate(jremoteSession, jprogress);
+ if (JNIUtil::isJavaExceptionThrown())
+ {
+ delete session;
+ jremoteSession = NULL;
+ }
+
+ return jremoteSession;
+}
+
+
+namespace{
+ struct compare_c_strings
+ {
+ bool operator()(const char* a, const char* b)
+ {
+ return (0 < std::strcmp(a, b));
+ }
+ };
+ typedef std::set<const char*, compare_c_strings> attempt_set;
+ typedef std::pair<attempt_set::iterator, bool> attempt_insert;
+} // anonymous namespace
+
+RemoteSession::RemoteSession(int retryAttempts,
+ const char* url, const char* uuid,
+ const char* configDirectory,
+ const char* username, const char* password,
+ Prompter::UniquePtr prompter,
+ jobject jcfgcb, jobject jtunnelcb)
+ : m_session(NULL), m_context(NULL)
+{
+ m_context = new RemoteSessionContext(
+ pool, configDirectory, username, password, prompter, jcfgcb, jtunnelcb);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ const char* corrected_url = NULL;
+ bool cycle_detected = false;
+ attempt_set attempted;
+
+ while (retryAttempts-- >= 0)
+ {
+ SVN_JNI_ERR(
+ svn_ra_open4(&m_session, &corrected_url,
+ url, uuid, m_context->getCallbacks(),
+ m_context->getCallbackBaton(),
+ m_context->getConfigData(),
+ pool.getPool()),
+ );
+
+ if (!corrected_url)
+ break;
+
+ attempt_insert result = attempted.insert(corrected_url);
+ if (!result.second)
+ {
+ cycle_detected = true;
+ break;
+ }
+
+ url = corrected_url;
+ corrected_url = NULL;
+ }
+
+ if (cycle_detected)
+ {
+ JNIEnv *env = JNIUtil::getEnv();
+
+ jstring exmsg = JNIUtil::makeJString(
+ apr_psprintf(pool.getPool(),
+ _("Redirect cycle detected for URL '%s'"),
+ corrected_url));
+
+ jclass excls = env->FindClass(
+ JAVAHL_CLASS("/SubversionException"));
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ static jmethodID exctor = 0;
+ if (exctor == 0)
+ {
+ exctor = env->GetMethodID(excls, "<init>", "(Ljava/lang/String;)V");
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ }
+
+ jobject ex = env->NewObject(excls, exctor, exmsg);
+ env->Throw(static_cast<jthrowable>(ex));
+ return;
+ }
+
+ if (corrected_url)
+ {
+ JNIEnv *env = JNIUtil::getEnv();
+
+ jstring exmsg = JNIUtil::makeJString(_("Too many redirects"));
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ jstring exurl = JNIUtil::makeJString(corrected_url);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ jclass excls = env->FindClass(
+ JAVAHL_CLASS("/remote/RetryOpenSession"));
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ static jmethodID exctor = 0;
+ if (exctor == 0)
+ {
+ exctor = env->GetMethodID(excls, "<init>", "(JJ)V");
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ }
+
+ jobject ex = env->NewObject(excls, exctor, exmsg, exurl);
+ env->Throw(static_cast<jthrowable>(ex));
+ return;
+ }
+}
+
+RemoteSession::~RemoteSession()
+{
+ delete m_context;
+}
+
+void
+RemoteSession::dispose(jobject jthis)
+{
+ static jfieldID fid = 0;
+ SVNBase::dispose(jthis, &fid, JAVA_CLASS_REMOTE_SESSION);
+}
+
+void RemoteSession::reparent(jstring jurl)
+{
+ SVN::Pool subPool(pool);
+ URL url(jurl, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return;
+ SVN_JNI_ERR(url.error_occurred(),);
+
+ SVN_JNI_ERR(svn_ra_reparent(m_session, url.c_str(), subPool.getPool()), );
+}
+
+jstring
+RemoteSession::getSessionUrl()
+{
+ SVN::Pool subPool(pool);
+ const char* url;
+ SVN_JNI_ERR(svn_ra_get_session_url(m_session, &url, subPool.getPool()), NULL);
+
+ jstring jurl = JNIUtil::makeJString(url);
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+
+ return jurl;
+}
+
+jstring
+RemoteSession::getSessionRelativePath(jstring jurl)
+{
+ SVN::Pool subPool(pool);
+ URL url(jurl, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ SVN_JNI_ERR(url.error_occurred(), NULL);
+
+ const char* rel_path;
+ SVN_JNI_ERR(svn_ra_get_path_relative_to_session(
+ m_session, &rel_path, url.c_str(), subPool.getPool()),
+ NULL);
+ jstring jrel_path = JNIUtil::makeJString(rel_path);
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+
+ return jrel_path;
+}
+
+jstring
+RemoteSession::getReposRelativePath(jstring jurl)
+{
+ SVN::Pool subPool(pool);
+ URL url(jurl, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ SVN_JNI_ERR(url.error_occurred(), NULL);
+
+ const char* rel_path;
+ SVN_JNI_ERR(svn_ra_get_path_relative_to_root(m_session, &rel_path,
+ url.c_str(),
+ subPool.getPool()),
+ NULL);
+
+ jstring jrel_path = JNIUtil::makeJString(rel_path);
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+
+ return jrel_path;
+}
+
+jstring
+RemoteSession::getReposUUID()
+{
+ SVN::Pool subPool(pool);
+ const char * uuid;
+ SVN_JNI_ERR(svn_ra_get_uuid2(m_session, &uuid, subPool.getPool()), NULL);
+
+ jstring juuid = JNIUtil::makeJString(uuid);
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+
+ return juuid;
+}
+
+jstring
+RemoteSession::getReposRootUrl()
+{
+ SVN::Pool subPool(pool);
+ const char* url;
+ SVN_JNI_ERR(svn_ra_get_repos_root2(m_session, &url, subPool.getPool()),
+ NULL);
+
+ jstring jurl = JNIUtil::makeJString(url);
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+
+ return jurl;
+}
+
+jlong
+RemoteSession::getLatestRevision()
+{
+ SVN::Pool subPool(pool);
+ svn_revnum_t rev;
+ SVN_JNI_ERR(svn_ra_get_latest_revnum(m_session, &rev, subPool.getPool()),
+ SVN_INVALID_REVNUM);
+ return rev;
+}
+
+jlong
+RemoteSession::getRevisionByTimestamp(jlong timestamp)
+{
+ SVN::Pool subPool(pool);
+ svn_revnum_t rev;
+ SVN_JNI_ERR(svn_ra_get_dated_revision(m_session, &rev,
+ apr_time_t(timestamp),
+ subPool.getPool()),
+ SVN_INVALID_REVNUM);
+ return rev;
+}
+
+namespace {
+svn_string_t*
+byte_array_to_svn_string(JNIByteArray& ary, SVN::Pool& scratch_pool)
+{
+ if (ary.isNull())
+ return NULL;
+ return svn_string_ncreate(reinterpret_cast<const char*>(ary.getBytes()),
+ ary.getLength(), scratch_pool.getPool());
+}
+} // anonymous namespace
+
+void
+RemoteSession::changeRevisionProperty(
+ jlong jrevision, jstring jname,
+ jbyteArray jold_value, jbyteArray jvalue)
+{
+ JNIStringHolder name(jname);
+ if (JNIUtil::isExceptionThrown())
+ return;
+
+ JNIByteArray old_value(jold_value);
+ if (JNIUtil::isExceptionThrown())
+ return;
+
+ JNIByteArray value(jvalue);
+ if (JNIUtil::isExceptionThrown())
+ return;
+
+ SVN::Pool subPool(pool);
+ svn_string_t* const* p_old_value = NULL;
+ svn_string_t* const str_old_value =
+ byte_array_to_svn_string(old_value, subPool);
+ if (str_old_value)
+ p_old_value = &str_old_value;
+
+ SVN_JNI_ERR(svn_ra_change_rev_prop2(m_session,
+ svn_revnum_t(jrevision),
+ name, p_old_value,
+ byte_array_to_svn_string(value, subPool),
+ subPool.getPool()), );
+}
+
+jobject
+RemoteSession::getRevisionProperties(jlong jrevision)
+{
+ SVN::Pool subPool(pool);
+ apr_hash_t *props;
+ SVN_JNI_ERR(svn_ra_rev_proplist(m_session, svn_revnum_t(jrevision),
+ &props, subPool.getPool()),
+ NULL);
+
+ return CreateJ::PropertyMap(props, subPool.getPool());
+}
+
+jbyteArray
+RemoteSession::getRevisionProperty(jlong jrevision, jstring jname)
+{
+ JNIStringHolder name(jname);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+
+ SVN::Pool subPool(pool);
+ svn_string_t *propval;
+ SVN_JNI_ERR(svn_ra_rev_prop(m_session, svn_revnum_t(jrevision),
+ name, &propval, subPool.getPool()),
+ NULL);
+
+ return JNIUtil::makeJByteArray(propval);
+}
+
+jlong
+RemoteSession::getFile(jlong jrevision, jstring jpath,
+ jobject jcontents, jobject jproperties)
+{
+ OutputStream contents_proxy(jcontents);
+ if (JNIUtil::isExceptionThrown())
+ return SVN_INVALID_REVNUM;
+
+ SVN::Pool subPool(pool);
+ Relpath path(jpath, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return SVN_INVALID_REVNUM;
+ SVN_JNI_ERR(path.error_occurred(), SVN_INVALID_REVNUM);
+
+ 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.c_str(), fetched_rev,
+ contents, &fetched_rev,
+ (jproperties ? &props : NULL),
+ subPool.getPool()),
+ SVN_INVALID_REVNUM);
+
+ if (jproperties)
+ {
+ CreateJ::FillPropertyMap(jproperties, props, subPool.getPool());
+ if (JNIUtil::isExceptionThrown())
+ return SVN_INVALID_REVNUM;
+ }
+
+ return fetched_rev;
+}
+
+namespace {
+void fill_dirents(const char* base_url, const char* base_relpath,
+ jobject jdirents, apr_hash_t* dirents,
+ apr_pool_t* scratch_pool)
+{
+ if (!dirents)
+ return;
+
+ base_url = apr_pstrcat(scratch_pool, base_url, "/", base_relpath, NULL);
+ base_url = svn_uri_canonicalize(base_url, scratch_pool);
+ svn_stringbuf_t* abs_path = svn_stringbuf_create(base_url, scratch_pool);
+ svn_stringbuf_appendbyte(abs_path, '/');
+ const apr_size_t base_len = abs_path->len;
+
+ JNIEnv *env = JNIUtil::getEnv();
+
+ // Create a local frame for our references
+ env->PushLocalFrame(LOCAL_FRAME_SIZE);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ // We have no way of knowing the exact type of `dirents' in advance
+ // so we cannot remember the "put" method ID across calls.
+ jmethodID put_mid =
+ env->GetMethodID(env->GetObjectClass(jdirents), "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)"
+ "Ljava/lang/Object;");
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NOTHING();
+
+ static jfieldID path_fid = 0;
+ if (path_fid == 0)
+ {
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/types/DirEntry"));
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NOTHING();
+
+ path_fid = env->GetFieldID(clazz, "path", "Ljava/lang/String;");
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NOTHING();
+ }
+
+ for (apr_hash_index_t* hi = apr_hash_first(scratch_pool, dirents);
+ hi; hi = apr_hash_next(hi))
+ {
+ const char* path;
+ svn_dirent_t* dirent;
+
+ const void *v_key;
+ void *v_val;
+
+ apr_hash_this(hi, &v_key, NULL, &v_val);
+ path = static_cast<const char*>(v_key);
+ dirent = static_cast<svn_dirent_t*>(v_val);
+ abs_path->len = base_len;
+ svn_stringbuf_appendcstr(abs_path, path);
+
+ jobject jdirent = CreateJ::DirEntry(path, abs_path->data, dirent);
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NOTHING();
+
+ // Use the existing DirEntry.path field as the key
+ jstring jpath = jstring(env->GetObjectField(jdirent, path_fid));
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NOTHING();
+
+ env->CallObjectMethod(jdirents, put_mid, jpath, jdirent);
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NOTHING();
+ env->DeleteLocalRef(jdirent);
+ }
+
+ POP_AND_RETURN_NOTHING();
+}
+} // anonymous namespace
+
+jlong
+RemoteSession::getDirectory(jlong jrevision, jstring jpath,
+ jint jdirent_fields, jobject jdirents,
+ jobject jproperties)
+{
+ SVN::Pool subPool(pool);
+ Relpath path(jpath, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return SVN_INVALID_REVNUM;
+ SVN_JNI_ERR(path.error_occurred(), SVN_INVALID_REVNUM);
+
+ 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.c_str(), fetched_rev,
+ apr_uint32_t(jdirent_fields),
+ subPool.getPool()),
+ SVN_INVALID_REVNUM);
+
+ if (jdirents)
+ {
+ // We will construct the absolute path in the DirEntry objects
+ // from the session URL and directory relpath.
+ const char* base_url;
+ SVN_JNI_ERR(svn_ra_get_session_url(m_session, &base_url,
+ subPool.getPool()),
+ SVN_INVALID_REVNUM);
+ fill_dirents(base_url, path.c_str(), jdirents, dirents,
+ subPool.getPool());
+ if (JNIUtil::isExceptionThrown())
+ return SVN_INVALID_REVNUM;
+ }
+
+ if (jproperties)
+ {
+ CreateJ::FillPropertyMap(jproperties, props, subPool.getPool());
+ if (JNIUtil::isExceptionThrown())
+ return SVN_INVALID_REVNUM;
+ }
+
+ return fetched_rev;
+}
+
+namespace {
+const apr_array_header_t*
+build_string_array(const Iterator& iter,
+ bool contains_relpaths, SVN::Pool& pool)
+{
+ apr_pool_t* result_pool = pool.getPool();
+ apr_array_header_t* array = apr_array_make(
+ result_pool, 0, sizeof(const char*));
+ while (iter.hasNext())
+ {
+ const char* element;
+ jstring jitem = (jstring)iter.next();
+ if (contains_relpaths)
+ {
+ Relpath item(jitem, pool);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ SVN_JNI_ERR(item.error_occurred(), NULL);
+ element = apr_pstrdup(result_pool, item.c_str());
+ }
+ else
+ {
+ JNIStringHolder item(jitem);
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+ element = item.pstrdup(result_pool);
+ }
+ APR_ARRAY_PUSH(array, const char*) = element;
+ }
+ return array;
+}
+}
+
+jobject
+RemoteSession::getMergeinfo(jobject jpaths, jlong jrevision, jobject jinherit,
+ jboolean jinclude_descendants)
+{
+ Iterator paths_iter(jpaths);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+
+ SVN::Pool subPool(pool);
+ const apr_array_header_t* paths =
+ build_string_array(paths_iter, true, subPool);
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+
+ svn_mergeinfo_catalog_t catalog;
+ SVN_JNI_ERR(svn_ra_get_mergeinfo(
+ m_session, &catalog, paths, svn_revnum_t(jrevision),
+ EnumMapper::toMergeinfoInheritance(jinherit),
+ bool(jinclude_descendants),
+ subPool.getPool()),
+ NULL);
+ if (catalog == NULL)
+ return NULL;
+
+ JNIEnv* env = JNIUtil::getEnv();
+ jclass cls = env->FindClass("java/util/HashMap");
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+
+ static jmethodID ctor_mid = 0;
+ if (0 == ctor_mid)
+ {
+ ctor_mid = env->GetMethodID(cls, "<init>", "()V");
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ }
+
+ static jmethodID put_mid = 0;
+ if (0 == put_mid)
+ {
+ put_mid = env->GetMethodID(cls, "put",
+ "(Ljava/lang/Object;"
+ "Ljava/lang/Object;)"
+ "Ljava/lang/Object;");
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ }
+
+ jobject jcatalog = env->NewObject(cls, ctor_mid);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+
+ for (apr_hash_index_t* hi = apr_hash_first(subPool.getPool(), catalog);
+ hi; hi = apr_hash_next(hi))
+ {
+ const void *v_key;
+ void *v_val;
+ apr_hash_this(hi, &v_key, NULL, &v_val);
+ const char* key = static_cast<const char*>(v_key);
+ svn_mergeinfo_t val = static_cast<svn_mergeinfo_t>(v_val);
+
+ jstring jpath = JNIUtil::makeJString(key);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ jobject jmergeinfo = CreateJ::Mergeinfo(val, subPool.getPool());
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+
+ env->CallObjectMethod(jcatalog, put_mid, jpath, jmergeinfo);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+
+ env->DeleteLocalRef(jpath);
+ env->DeleteLocalRef(jmergeinfo);
+ }
+
+ return jcatalog;
+}
+
+// TODO: update
+// TODO: switch
+
+namespace {
+svn_error_t*
+status_unlock_func(void* baton, const char* path, apr_pool_t* scratch_pool)
+{
+ //DEBUG:fprintf(stderr, " (n) status_unlock_func('%s')\n", path);
+ return SVN_NO_ERROR;
+}
+
+svn_error_t*
+status_fetch_props_func(apr_hash_t **props, void *baton,
+ const char *path, svn_revnum_t base_revision,
+ apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+ //DEBUG:fprintf(stderr, " (n) status_fetch_props_func('%s', r%lld)\n",
+ //DEBUG: path, static_cast<long long>(base_revision));
+ *props = apr_hash_make(result_pool);
+ return SVN_NO_ERROR;
+}
+
+svn_error_t*
+status_fetch_base_func(const char **filename, void *baton,
+ const char *path, svn_revnum_t base_revision,
+ apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+ //DEBUG:fprintf(stderr, " (n) status_fetch_base_func('%s', r%lld)\n",
+ //DEBUG: path, static_cast<long long>(base_revision));
+ *filename = NULL;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t*
+status_start_edit_func(void* baton, svn_revnum_t start_revision)
+{
+ //DEBUG:fprintf(stderr, " (n) status_start_edit_func(r%lld)\n",
+ //DEBUG: static_cast<long long>(start_revision));
+ return SVN_NO_ERROR;
+}
+
+svn_error_t*
+status_target_revision_func(void* baton, svn_revnum_t target_revision,
+ apr_pool_t* scratch_pool)
+{
+ //DEBUG:fprintf(stderr, " (n) status_target_revision_func(r%lld)\n",
+ //DEBUG: static_cast<long long>(target_revision));
+ *static_cast<svn_revnum_t*>(baton) = target_revision;
+ return SVN_NO_ERROR;
+}
+
+const EditorProxyCallbacks template_status_editor_callbacks = {
+ status_unlock_func,
+ status_fetch_props_func,
+ status_fetch_base_func,
+ { status_start_edit_func, status_target_revision_func, NULL },
+ NULL
+};
+} // anonymous namespace
+
+void
+RemoteSession::status(jobject jthis, jstring jstatus_target,
+ jlong jrevision, jobject jdepth,
+ jobject jstatus_editor, jobject jreporter)
+{
+ StateReporter *rp = StateReporter::getCppObject(jreporter);
+ CPPADDR_NULL_PTR(rp,);
+
+ SVN::Pool scratchPool(rp->get_report_pool());
+ Relpath status_target(jstatus_target, scratchPool);
+ if (JNIUtil::isExceptionThrown())
+ return;
+
+ apr_pool_t* scratch_pool = scratchPool.getPool();
+ const char* repos_root_url;
+ SVN_JNI_ERR(svn_ra_get_repos_root2(m_session, &repos_root_url,
+ scratch_pool),);
+ const char* session_root_url;
+ SVN_JNI_ERR(svn_ra_get_session_url(m_session, &session_root_url,
+ scratch_pool),);
+ const char* base_relpath;
+ SVN_JNI_ERR(svn_ra_get_path_relative_to_root(m_session, &base_relpath,
+ session_root_url,
+ scratch_pool),);
+
+ EditorProxyCallbacks proxy_callbacks =
+ template_status_editor_callbacks;
+ proxy_callbacks.m_extra_baton.baton = &rp->m_target_revision;
+
+ apr_pool_t* report_pool = rp->get_report_pool();
+ std::auto_ptr<EditorProxy> editor(
+ new EditorProxy(jstatus_editor, report_pool,
+ repos_root_url, base_relpath,
+ m_context->checkCancel, m_context,
+ proxy_callbacks));
+ if (JNIUtil::isExceptionThrown())
+ return;
+
+ const svn_ra_reporter3_t* raw_reporter;
+ void* report_baton;
+ SVN_JNI_ERR(svn_ra_do_status2(m_session,
+ &raw_reporter, &report_baton,
+ status_target.c_str(),
+ svn_revnum_t(jrevision),
+ EnumMapper::toDepth(jdepth),
+ editor->delta_editor(),
+ editor->delta_baton(),
+ report_pool),);
+ rp->set_reporter_data(raw_reporter, report_baton, editor.release());
+}
+
+// TODO: diff
+
+void
+RemoteSession::getLog(jobject jpaths,
+ jlong jstartrev, jlong jendrev, jint jlimit,
+ jboolean jstrict_node_history,
+ jboolean jdiscover_changed_paths,
+ jboolean jinclude_merged_revisions,
+ jobject jrevprops, jobject jlog_callback)
+{
+ Iterator pathiter(jpaths);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ Iterator revpropiter(jrevprops);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ LogMessageCallback receiver(jlog_callback);
+
+ SVN::Pool subPool(pool);
+ const apr_array_header_t* paths = build_string_array(pathiter,
+ true, subPool);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ const apr_array_header_t* revprops = (jrevprops != NULL)
+ ? build_string_array(revpropiter,
+ false, subPool)
+ : NULL;
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ SVN_JNI_ERR(svn_ra_get_log2(m_session, paths,
+ svn_revnum_t(jstartrev), svn_revnum_t(jendrev),
+ int(jlimit),
+ bool(jdiscover_changed_paths),
+ bool(jstrict_node_history),
+ bool(jinclude_merged_revisions),
+ revprops,
+ receiver.callback, &receiver,
+ subPool.getPool()),);
+}
+
+jobject
+RemoteSession::checkPath(jstring jpath, jlong jrevision)
+{
+ SVN::Pool subPool(pool);
+ Relpath path(jpath, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ SVN_JNI_ERR(path.error_occurred(), NULL);
+
+ svn_node_kind_t kind;
+ SVN_JNI_ERR(svn_ra_check_path(m_session, path.c_str(),
+ svn_revnum_t(jrevision),
+ &kind, subPool.getPool()),
+ NULL);
+
+ return EnumMapper::mapNodeKind(kind);
+}
+
+jobject
+RemoteSession::stat(jstring jpath, jlong jrevision)
+{
+ SVN::Pool subPool(pool);
+ Relpath path(jpath, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ SVN_JNI_ERR(path.error_occurred(), NULL);
+
+ svn_dirent_t* dirent;
+ SVN_JNI_ERR(svn_ra_stat(m_session, path.c_str(),
+ svn_revnum_t(jrevision),
+ &dirent, subPool.getPool()),
+ NULL);
+
+ if (dirent)
+ return CreateJ::DirEntry(path.c_str(), path.c_str(), dirent);
+ return NULL;
+}
+
+namespace {
+apr_array_header_t*
+long_iterable_to_revnum_array(jobject jlong_iterable, apr_pool_t* pool)
+{
+
+ JNIEnv* env = JNIUtil::getEnv();
+
+ jclass cls = env->FindClass("java/lang/Long");
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+
+ static jmethodID mid = 0;
+ if (0 == mid)
+ {
+ mid = env->GetMethodID(cls, "longValue", "()J");
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ }
+
+ apr_array_header_t* array = apr_array_make(pool, 0, sizeof(svn_revnum_t));
+ Iterator iter(jlong_iterable);
+ while (iter.hasNext())
+ {
+ const jlong entry = env->CallLongMethod(iter.next(), mid);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ APR_ARRAY_PUSH(array, svn_revnum_t) = svn_revnum_t(entry);
+ }
+ return array;
+}
+
+jobject
+location_hash_to_map(apr_hash_t* locations, apr_pool_t* scratch_pool)
+{
+ JNIEnv* env = JNIUtil::getEnv();
+
+ jclass long_cls = env->FindClass("java/lang/Long");
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+
+ static jmethodID long_ctor = 0;
+ if (0 == long_ctor)
+ {
+ long_ctor = env->GetMethodID(long_cls, "<init>", "(J)V");
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ }
+
+ jclass hash_cls = env->FindClass("java/util/HashMap");
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+
+ static jmethodID hash_ctor = 0;
+ if (0 == hash_ctor)
+ {
+ hash_ctor = env->GetMethodID(hash_cls, "<init>", "()V");
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ }
+
+ static jmethodID hash_put = 0;
+ if (0 == hash_put)
+ {
+ hash_put = env->GetMethodID(hash_cls, "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;"
+ ")Ljava/lang/Object;");
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ }
+
+ jobject result = env->NewObject(hash_cls, hash_ctor);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+
+ if (!locations)
+ return result;
+
+ for (apr_hash_index_t* hi = apr_hash_first(scratch_pool, locations);
+ hi; hi = apr_hash_next(hi))
+ {
+ const void* key;
+ void* val;
+
+ apr_hash_this(hi, &key, NULL, &val);
+
+ jobject jkey = env->NewObject(
+ long_cls, long_ctor, jlong(*static_cast<const svn_revnum_t*>(key)));
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ jstring jval = JNIUtil::makeJString(static_cast<const char*>(val));
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+
+ env->CallObjectMethod(result, hash_put, jkey, jval);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+
+ env->DeleteLocalRef(jkey);
+ env->DeleteLocalRef(jval);
+ }
+
+ return result;
+}
+} // anonymous namespace
+
+jobject
+RemoteSession::getLocations(jstring jpath, jlong jpeg_revision,
+ jobject jlocation_revisions)
+{
+ if (!jpath || !jlocation_revisions)
+ return NULL;
+
+ SVN::Pool subPool(pool);
+ Relpath path(jpath, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ SVN_JNI_ERR(path.error_occurred(), NULL);
+
+ apr_array_header_t* location_revisions =
+ long_iterable_to_revnum_array(jlocation_revisions, subPool.getPool());
+ if (!location_revisions)
+ return NULL;
+
+ apr_hash_t* locations;
+ SVN_JNI_ERR(svn_ra_get_locations(m_session, &locations,
+ path.c_str(), svn_revnum_t(jpeg_revision),
+ location_revisions, subPool.getPool()),
+ NULL);
+ return location_hash_to_map(locations, subPool.getPool());
+}
+
+namespace {
+class LocationSegmentHandler
+{
+public:
+ static svn_error_t* callback(svn_location_segment_t* segment,
+ void* baton, apr_pool_t*)
+ {
+ LocationSegmentHandler* const self =
+ static_cast<LocationSegmentHandler*>(baton);
+ SVN_ERR_ASSERT(self->m_jcallback != NULL);
+ self->call(segment);
+ SVN_ERR(JNIUtil::checkJavaException(SVN_ERR_BASE));
+ return SVN_NO_ERROR;
+ }
+
+ LocationSegmentHandler(jobject jcallback)
+ : m_jcallback(jcallback),
+ m_call_mid(0)
+ {
+ JNIEnv* env = JNIUtil::getEnv();
+ jclass cls = env->GetObjectClass(jcallback);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ m_call_mid = env->GetMethodID(
+ cls, "doSegment", "(" JAVAHL_ARG("/ISVNRemote$LocationSegment;") ")V");
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ }
+
+private:
+ void call(svn_location_segment_t* segment)
+ {
+ JNIEnv* env = JNIUtil::getEnv();
+ jclass cls = env->FindClass(JAVAHL_CLASS("/ISVNRemote$LocationSegment"));
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ static jmethodID mid = 0;
+ if (mid == 0)
+ {
+ mid = env->GetMethodID(cls, "<init>",
+ "(Ljava/lang/String;JJ)V");
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ }
+
+ jstring jpath = JNIUtil::makeJString(segment->path);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ env->CallVoidMethod(m_jcallback, m_call_mid,
+ env->NewObject(cls, mid, jpath,
+ jlong(segment->range_start),
+ jlong(segment->range_end)));
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ env->DeleteLocalRef(jpath);
+ }
+
+ jobject m_jcallback;
+ jmethodID m_call_mid;
+};
+} // anonymous namespace
+
+void
+RemoteSession::getLocationSegments(jstring jpath, jlong jpeg_revision,
+ jlong jstart_revision, jlong jend_revision,
+ jobject jcallback)
+{
+ SVN::Pool subPool(pool);
+ Relpath path(jpath, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return;
+ SVN_JNI_ERR(path.error_occurred(), );
+
+ LocationSegmentHandler handler(jcallback);
+ if (JNIUtil::isExceptionThrown())
+ return ;
+
+ SVN_JNI_ERR(svn_ra_get_location_segments(m_session, path.c_str(),
+ svn_revnum_t(jpeg_revision),
+ svn_revnum_t(jstart_revision),
+ svn_revnum_t(jend_revision),
+ handler.callback, &handler,
+ subPool.getPool()),);
+}
+
+namespace {
+class FileRevisionHandler
+{
+public:
+ static svn_error_t* callback(void* baton,
+ const char* path, svn_revnum_t revision,
+ apr_hash_t* revision_props,
+ // We ignore the deltas as they're not
+ // exposed in the JavaHL API.
+ svn_boolean_t result_of_merge,
+ svn_txdelta_window_handler_t* delta_handler,
+ void** delta_handler_baton,
+ apr_array_header_t* prop_diffs,
+ apr_pool_t* scratch_pool)
+ {
+ if (delta_handler)
+ *delta_handler = svn_delta_noop_window_handler;
+ if (delta_handler_baton)
+ *delta_handler_baton = NULL;
+
+ FileRevisionHandler* const self =
+ static_cast<FileRevisionHandler*>(baton);
+ SVN_ERR_ASSERT(self->m_jcallback != NULL);
+ self->call(path, revision, revision_props,
+ result_of_merge, prop_diffs,
+ (delta_handler != NULL),
+ scratch_pool);
+ SVN_ERR(JNIUtil::checkJavaException(SVN_ERR_BASE));
+ return SVN_NO_ERROR;
+ }
+
+ FileRevisionHandler(jobject jcallback)
+ : m_jcallback(jcallback),
+ m_call_mid(0)
+ {
+ JNIEnv* env = JNIUtil::getEnv();
+ jclass cls = env->GetObjectClass(jcallback);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ m_call_mid = env->GetMethodID(
+ cls, "doRevision", "(" JAVAHL_ARG("/ISVNRemote$FileRevision;") ")V");
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ }
+
+private:
+ void call(const char* path, svn_revnum_t revision,
+ apr_hash_t* revision_props,
+ svn_boolean_t result_of_merge,
+ apr_array_header_t* prop_diffs,
+ svn_boolean_t has_text_delta,
+ apr_pool_t* scratch_pool)
+ {
+ JNIEnv* env = JNIUtil::getEnv();
+ jclass cls = env->FindClass(JAVAHL_CLASS("/ISVNRemote$FileRevision"));
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ static jmethodID mid = 0;
+ if (mid == 0)
+ {
+ mid = env->GetMethodID(cls, "<init>",
+ "(Ljava/lang/String;JZ"
+ "Ljava/util/Map;Ljava/util/Map;Z)V");
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ }
+
+ jstring jpath = JNIUtil::makeJString(path);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ jobject jrevprops = CreateJ::PropertyMap(revision_props, scratch_pool);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ jobject jpropdelta = CreateJ::PropertyMap(prop_diffs, scratch_pool);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ env->CallVoidMethod(m_jcallback, m_call_mid,
+ env->NewObject(cls, mid, jpath, jlong(revision),
+ jboolean(result_of_merge),
+ jrevprops, jpropdelta,
+ jboolean(has_text_delta)));
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ env->DeleteLocalRef(jpath);
+ env->DeleteLocalRef(jrevprops);
+ env->DeleteLocalRef(jpropdelta);
+ }
+
+ jobject m_jcallback;
+ jmethodID m_call_mid;
+};
+} // anonymous namespace
+
+void
+RemoteSession::getFileRevisions(jstring jpath,
+ jlong jstart_revision, jlong jend_revision,
+ jboolean jinclude_merged_revisions,
+ jobject jcallback)
+{
+ SVN::Pool subPool(pool);
+ Relpath path(jpath, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return;
+ SVN_JNI_ERR(path.error_occurred(), );
+
+ FileRevisionHandler handler(jcallback);
+ if (JNIUtil::isExceptionThrown())
+ return;
+
+ SVN_JNI_ERR(svn_ra_get_file_revs2(m_session, path.c_str(),
+ svn_revnum_t(jstart_revision),
+ svn_revnum_t(jend_revision),
+ bool(jinclude_merged_revisions),
+ handler.callback, &handler,
+ subPool.getPool()),);
+}
+
+// TODO: lock
+// TODO: unlock
+// TODO: getLock
+
+jobject
+RemoteSession::getLocks(jstring jpath, jobject jdepth)
+{
+ svn_depth_t depth = EnumMapper::toDepth(jdepth);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+
+ SVN::Pool subPool(pool);
+ Relpath path(jpath, subPool);
+ if (JNIUtil::isExceptionThrown())
+ return NULL;
+ SVN_JNI_ERR(path.error_occurred(), NULL);
+
+ apr_hash_t *locks;
+ SVN_JNI_ERR(svn_ra_get_locks2(m_session, &locks, path.c_str(), depth,
+ subPool.getPool()),
+ NULL);
+
+ return CreateJ::LockMap(locks, subPool.getPool());
+}
+
+// TODO: replayRange
+// TODO: replay
+// TODO: getDeletedRevision
+// TODO: getInheritedProperties
+
+jboolean
+RemoteSession::hasCapability(jstring jcapability)
+{
+ JNIStringHolder capability(jcapability);
+ if (JNIUtil::isExceptionThrown())
+ return false;
+
+ SVN::Pool subPool(pool);
+ svn_boolean_t has;
+ SVN_JNI_ERR(svn_ra_has_capability(m_session, &has, capability,
+ subPool.getPool()),
+ false);
+
+ return jboolean(has);
+}
diff --git a/subversion/bindings/javahl/native/RemoteSession.h b/subversion/bindings/javahl/native/RemoteSession.h
new file mode 100644
index 0000000..26ca402
--- /dev/null
+++ b/subversion/bindings/javahl/native/RemoteSession.h
@@ -0,0 +1,128 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file RemoteSession.h
+ * @brief Interface of the class RemoteSession
+ */
+
+#ifndef JAVAHL_REMOTE_SESSION_H
+#define JAVAHL_REMOTE_SESSION_H
+
+#include <jni.h>
+
+#include "svn_ra.h"
+
+#include "SVNBase.h"
+#include "RemoteSessionContext.h"
+#include "Prompter.h"
+
+class CommitEditor;
+
+/*
+ * This class wraps Ra based operations from svn_ra.h
+ */
+class RemoteSession : public SVNBase
+{
+ public:
+ static RemoteSession* getCppObject(jobject jthis);
+ static jobject open(jint jretryAttempts,
+ jstring jurl, jstring juuid,
+ jstring jconfigDirectory,
+ jstring jusername, jstring jpassword,
+ jobject jprompter, jobject jdeprecatedPrompter,
+ jobject jprogress, jobject jcfgcb, jobject jtunnelcb);
+ static jobject open(jint jretryAttempts,
+ const char* url, const char* uuid,
+ const char* configDirectory,
+ const char* username, const char* password,
+ Prompter::UniquePtr prompter, jobject jprogress,
+ jobject jcfgcb, jobject jtunnelcb);
+ ~RemoteSession();
+
+ void cancelOperation() const { m_context->cancelOperation(); }
+
+ virtual void dispose(jobject jthis);
+
+ void reparent(jstring jurl);
+ jstring getSessionUrl();
+ jstring getSessionRelativePath(jstring jurl);
+ jstring getReposRelativePath(jstring jurl);
+ jstring getReposUUID();
+ jstring getReposRootUrl();
+ jlong getLatestRevision();
+ jlong getRevisionByTimestamp(jlong jtimestamp);
+ void changeRevisionProperty(jlong jrevision, jstring jname,
+ jbyteArray jold_value,
+ jbyteArray jvalue);
+ jobject getRevisionProperties(jlong jrevision);
+ jbyteArray getRevisionProperty(jlong jrevision, jstring jname);
+ jlong getFile(jlong jrevision, jstring jpath,
+ jobject jcontents, jobject jproperties);
+ jlong getDirectory(jlong jrevision, jstring jpath, jint jdirent_fields,
+ jobject jdirents, jobject jproperties);
+ jobject getMergeinfo(jobject jpaths, jlong jrevision, jobject jinherit,
+ jboolean jinclude_descendants);
+ // TODO: update
+ // TODO: switch
+ void status(jobject jthis, jstring jstatus_target,
+ jlong jrevision, jobject jdepth,
+ jobject jstatus_editor, jobject jreporter);
+ // TODO: diff
+ void getLog(jobject jpaths, jlong jstartrev, jlong jendrev, jint jlimit,
+ jboolean jstrict_node_history, jboolean jdiscover_changed_paths,
+ jboolean jinclude_merged_revisions,
+ jobject jrevprops, jobject jlog_callback);
+ jobject checkPath(jstring jpath, jlong jrevision);
+ jobject stat(jstring jpath, jlong jrevision);
+ jobject getLocations(jstring jpath, jlong jpeg_revision,
+ jobject jlocation_revisions);
+ void getLocationSegments(jstring jpath, jlong jpeg_revision,
+ jlong jstart_revision, jlong jend_revision,
+ jobject jcallback);
+ void getFileRevisions(jstring jpath,
+ jlong jstart_revision, jlong jend_revision,
+ jboolean jinclude_merged_revisions,
+ jobject jcallback);
+ // TODO: getFileRevisions
+ // TODO: lock
+ // TODO: unlock
+ // TODO: getLock
+ jobject getLocks(jstring jpath, jobject jdepth);
+ // TODO: replayRange
+ // TODO: replay
+ // TODO: getDeletedRevision
+ // TODO: getInheritedProperties
+ jboolean hasCapability(jstring capability);
+
+ private:
+ friend class CommitEditor;
+ RemoteSession(int retryAttempts,
+ const char* url, const char* uuid,
+ const char* configDirectory,
+ const char* username, const char* password,
+ Prompter::UniquePtr prompter, jobject jcfgcb, jobject jtunnelcb);
+
+ svn_ra_session_t* m_session;
+ RemoteSessionContext* m_context;
+};
+
+#endif // JAVAHL_REMOTE_SESSION_H
diff --git a/subversion/bindings/javahl/native/RemoteSessionContext.cpp b/subversion/bindings/javahl/native/RemoteSessionContext.cpp
new file mode 100644
index 0000000..e4b86e1
--- /dev/null
+++ b/subversion/bindings/javahl/native/RemoteSessionContext.cpp
@@ -0,0 +1,125 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file RemoteSessionContext.cpp
+ * @brief Implementation of the class RemoteSessionContext
+ */
+
+
+#include "RemoteSessionContext.h"
+#include "JNIUtil.h"
+#include "Prompter.h"
+
+
+RemoteSessionContext::RemoteSessionContext(
+ SVN::Pool &pool, const char* configDirectory,
+ const char* usernameStr, const char* passwordStr,
+ Prompter::UniquePtr prompter, jobject jcfgcb, jobject jtunnelcb)
+ : OperationContext(pool), m_raCallbacks(NULL)
+{
+ setConfigDirectory(configDirectory);
+ if (usernameStr != NULL)
+ username(usernameStr);
+
+ if (passwordStr != NULL)
+ password(passwordStr);
+
+ setPrompt(prompter);
+ setConfigEventHandler(jcfgcb);
+ setTunnelCallback(jtunnelcb);
+
+ /*
+ * Setup callbacks
+ */
+ SVN_JNI_ERR(svn_ra_create_callbacks(&m_raCallbacks, m_pool->getPool()), );
+
+ m_raCallbacks->auth_baton = getAuthBaton(pool);
+ m_raCallbacks->cancel_func = checkCancel;
+ m_raCallbacks->get_client_string = clientName;
+ m_raCallbacks->progress_baton = NULL;
+ m_raCallbacks->progress_func = progress;
+
+ /*
+ * JNI RA layer does not work with WC so all WC callbacks are set to NULL
+ */
+ m_raCallbacks->get_wc_prop = NULL;
+ m_raCallbacks->invalidate_wc_props = NULL;
+ m_raCallbacks->push_wc_prop = NULL;
+ m_raCallbacks->set_wc_prop = NULL;
+
+ /*
+ * Don't set deprecated callback
+ */
+ m_raCallbacks->open_tmp_file = NULL;
+
+ if (m_jtunnelcb)
+ {
+ m_raCallbacks->check_tunnel_func = checkTunnel;
+ m_raCallbacks->open_tunnel_func = openTunnel;
+ m_raCallbacks->tunnel_baton = m_jtunnelcb;
+ }
+}
+
+RemoteSessionContext::~RemoteSessionContext()
+{
+}
+
+void RemoteSessionContext::activate(jobject jremoteSession, jobject jprogress)
+{
+ /*
+ * Attach session context java object
+ */
+ static jfieldID ctxFieldID = 0;
+ attachJavaObject(jremoteSession,
+ JAVAHL_ARG("/remote/RemoteSession$RemoteSessionContext;"),
+ "sessionContext", &ctxFieldID);
+
+ /*
+ * Set the progress callback
+ */
+ JNIEnv *env = JNIUtil::getEnv();
+
+ jclass clazz = env->GetObjectClass(m_jctx);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ jmethodID mid = env->GetMethodID(
+ clazz, "setProgressCallback",
+ "(" JAVAHL_ARG("/callback/ProgressCallback;") ")V");
+ if (JNIUtil::isJavaExceptionThrown() || mid == 0)
+ return;
+
+ env->CallVoidMethod(m_jctx, mid, jprogress);
+ m_raCallbacks->progress_baton = m_jctx;
+}
+
+void *
+RemoteSessionContext::getCallbackBaton()
+{
+ return this;
+}
+
+svn_ra_callbacks2_t *
+RemoteSessionContext::getCallbacks()
+{
+ return m_raCallbacks;
+}
diff --git a/subversion/bindings/javahl/native/RemoteSessionContext.h b/subversion/bindings/javahl/native/RemoteSessionContext.h
new file mode 100644
index 0000000..8c569a1
--- /dev/null
+++ b/subversion/bindings/javahl/native/RemoteSessionContext.h
@@ -0,0 +1,51 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file RemoteSessionContext.h
+ * @brief Interface of the class RemoteSessionContext
+ */
+
+#ifndef JAVAHL_REMOTE_SESSION_CONTEXT_H
+#define JAVAHL_REMOTE_SESSION_CONTEXT_H
+
+#include "svn_ra.h"
+
+#include "OperationContext.h"
+
+class RemoteSessionContext : public OperationContext
+{
+ public:
+ RemoteSessionContext(SVN::Pool &pool,
+ const char* jconfigDirectory,
+ const char* jusername, const char* jpassword,
+ std::auto_ptr<Prompter> prompter,
+ jobject jcfgcb, jobject jtunnelcb);
+ virtual ~RemoteSessionContext();
+ void activate(jobject jremoteSession, jobject jprogress);
+ void * getCallbackBaton();
+ svn_ra_callbacks2_t* getCallbacks();
+
+ private:
+ svn_ra_callbacks2_t* m_raCallbacks;
+};
+
+#endif /* JAVAHL_REMOTE_SESSION_CONTEXT_H */
diff --git a/subversion/bindings/javahl/native/ReposFreezeAction.cpp b/subversion/bindings/javahl/native/ReposFreezeAction.cpp
index 8c1cb7e..78f307f 100644
--- a/subversion/bindings/javahl/native/ReposFreezeAction.cpp
+++ b/subversion/bindings/javahl/native/ReposFreezeAction.cpp
@@ -39,7 +39,7 @@ svn_error_t* ReposFreezeAction::invoke()
static volatile jmethodID mid = 0;
if (!mid)
{
- jclass cls = env->FindClass(JAVA_PACKAGE"/callback/ReposFreezeAction");
+ jclass cls = env->FindClass(JAVAHL_CLASS("/callback/ReposFreezeAction"));
if (!JNIUtil::isJavaExceptionThrown())
mid = env->GetMethodID(cls, "invoke", "()V");
}
diff --git a/subversion/bindings/javahl/native/ReposNotifyCallback.cpp b/subversion/bindings/javahl/native/ReposNotifyCallback.cpp
index a716653..1237d13 100644
--- a/subversion/bindings/javahl/native/ReposNotifyCallback.cpp
+++ b/subversion/bindings/javahl/native/ReposNotifyCallback.cpp
@@ -69,12 +69,12 @@ ReposNotifyCallback::onNotify(const svn_repos_notify_t *wcNotify,
static jmethodID mid = 0;
if (mid == 0)
{
- jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/ReposNotifyCallback");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/ReposNotifyCallback"));
if (JNIUtil::isJavaExceptionThrown())
return;
mid = env->GetMethodID(clazz, "onNotify",
- "(L"JAVA_PACKAGE"/ReposNotifyInformation;)V");
+ "(" JAVAHL_ARG("/ReposNotifyInformation;") ")V");
if (JNIUtil::isJavaExceptionThrown() || mid == 0)
return;
diff --git a/subversion/bindings/javahl/native/ReposVerifyCallback.cpp b/subversion/bindings/javahl/native/ReposVerifyCallback.cpp
new file mode 100644
index 0000000..1fb6ecd
--- /dev/null
+++ b/subversion/bindings/javahl/native/ReposVerifyCallback.cpp
@@ -0,0 +1,88 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file ReposVerifyCallback.cpp
+ * @brief Implementation of the class ReposVerifyCallback
+ */
+
+#include "ReposVerifyCallback.h"
+#include "JNIUtil.h"
+
+ReposVerifyCallback::ReposVerifyCallback(jobject jverify_cb)
+ : m_jverify_cb(jverify_cb)
+{}
+
+ReposVerifyCallback::~ReposVerifyCallback()
+{
+ // Don't need to destroy the reference, since it was given us by Java
+}
+
+svn_error_t *
+ReposVerifyCallback::callback(void *baton,
+ svn_revnum_t revision,
+ svn_error_t *verify_err,
+ apr_pool_t *scratch_pool)
+{
+ if (!baton)
+ return SVN_NO_ERROR;
+
+ static_cast<ReposVerifyCallback*>(baton)
+ ->onVerifyError(revision, verify_err, scratch_pool);
+ if (JNIUtil::isJavaExceptionThrown())
+ return JNIUtil::wrapJavaException();
+ return SVN_NO_ERROR;
+}
+
+void
+ReposVerifyCallback::onVerifyError(svn_revnum_t revision,
+ svn_error_t *verify_err,
+ apr_pool_t *scratch_pool)
+{
+ JNIEnv *env = JNIUtil::getEnv();
+
+ // Java method id will not change during the time this library is
+ // loaded, so it can be cached.
+ static jmethodID mid = 0;
+ if (mid == 0)
+ {
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/ReposVerifyCallback"));
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ mid = env->GetMethodID(clazz, "onVerifyError",
+ "(J" JAVAHL_ARG("/ClientException;") ")V");
+ if (JNIUtil::isJavaExceptionThrown() || mid == 0)
+ return;
+
+ env->DeleteLocalRef(clazz);
+ }
+
+ jthrowable jverify_err = NULL;
+ if (verify_err)
+ jverify_err = JNIUtil::createClientException(svn_error_dup(verify_err), NULL);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ env->CallVoidMethod(m_jverify_cb, mid, jlong(revision), jverify_err);
+ if (verify_err)
+ env->DeleteLocalRef(jverify_err);
+}
diff --git a/subversion/bindings/javahl/native/ReposVerifyCallback.h b/subversion/bindings/javahl/native/ReposVerifyCallback.h
new file mode 100644
index 0000000..a89d204
--- /dev/null
+++ b/subversion/bindings/javahl/native/ReposVerifyCallback.h
@@ -0,0 +1,75 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file ReposVerifyCallback.h
+ * @brief Interface of the class ReposVerifyCallback
+ */
+
+#ifndef SVN_JAVAHL_REPOS_VERIFY_CALLBACK_H
+#define SVN_JAVAHL_REPOS_VERIFY_CALLBACK_H
+
+#include <jni.h>
+#include "svn_repos.h"
+
+/**
+ * This class passes notification from subversion to a Java object
+ * (1.2 version).
+ */
+class ReposVerifyCallback
+{
+ private:
+ /**
+ * The local reference to the Java object.
+ */
+ jobject m_jverify_cb;
+
+ public:
+ /**
+ * Create a new object and store the Java object.
+ * @param verify_cb global reference to the Java object
+ */
+ ReposVerifyCallback(jobject jverify_cb);
+
+ ~ReposVerifyCallback();
+
+ /**
+ * Implementation of the svn_repos_verify_callback_t API.
+ *
+ * @param baton Notification instance is passed using this parameter
+ * @param revision The revision that the error was emitted for
+ * @param verify_err The emitted error
+ * @param scratch_pool An APR pool from which to allocate memory.
+ */
+ static svn_error_t * callback(void *baton,
+ svn_revnum_t revision,
+ svn_error_t *verify_err,
+ apr_pool_t *scratch_pool);
+
+ /**
+ * Handler for Subversion notifications.
+ */
+ void onVerifyError(svn_revnum_t revision,
+ svn_error_t *verify_err,
+ apr_pool_t *scratch_pool);
+};
+
+#endif // SVN_JAVAHL_REPOS_VERIFY_CALLBACK_H
diff --git a/subversion/bindings/javahl/native/Revision.cpp b/subversion/bindings/javahl/native/Revision.cpp
index 7ba2094..d9e52f8 100644
--- a/subversion/bindings/javahl/native/Revision.cpp
+++ b/subversion/bindings/javahl/native/Revision.cpp
@@ -57,12 +57,12 @@ Revision::Revision(jobject jthis, bool headIfUnspecified,
static jfieldID fid = 0;
if (fid == 0)
{
- jclass clazz = env->FindClass(JAVA_PACKAGE"/types/Revision");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/types/Revision"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NOTHING();
fid = env->GetFieldID(clazz, "revKind",
- "L"JAVA_PACKAGE"/types/Revision$Kind;");
+ JAVAHL_ARG("/types/Revision$Kind;"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NOTHING();
}
@@ -81,7 +81,7 @@ Revision::Revision(jobject jthis, bool headIfUnspecified,
if (fidNum == 0)
{
jclass clazz =
- env->FindClass(JAVA_PACKAGE"/types/Revision$Number");
+ env->FindClass(JAVAHL_CLASS("/types/Revision$Number"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NOTHING();
@@ -99,7 +99,7 @@ Revision::Revision(jobject jthis, bool headIfUnspecified,
if (fidDate == 0)
{
jclass clazz =
- env->FindClass(JAVA_PACKAGE"/types/Revision$DateSpec");
+ env->FindClass(JAVAHL_CLASS("/types/Revision$DateSpec"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN_NOTHING();
@@ -158,7 +158,7 @@ jobject
Revision::makeJRevision(svn_revnum_t rev)
{
JNIEnv *env = JNIUtil::getEnv();
- jclass clazz = env->FindClass(JAVA_PACKAGE "/types/Revision");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/types/Revision"));
if (JNIUtil::isJavaExceptionThrown())
return NULL;
@@ -166,7 +166,7 @@ Revision::makeJRevision(svn_revnum_t rev)
if (getInstance == 0)
{
getInstance = env->GetStaticMethodID(clazz, "getInstance",
- "(J)L" JAVA_PACKAGE "/types/Revision;");
+ "(J)" JAVAHL_ARG("/types/Revision;"));
if (JNIUtil::isExceptionThrown())
return NULL;
}
diff --git a/subversion/bindings/javahl/native/Revision.h b/subversion/bindings/javahl/native/Revision.h
index bef87a9..d98a868 100644
--- a/subversion/bindings/javahl/native/Revision.h
+++ b/subversion/bindings/javahl/native/Revision.h
@@ -50,6 +50,7 @@ class Revision
* Make a Revision Java object.
*/
static jobject makeJRevision(svn_revnum_t rev);
+ static jobject makeJRevision(const svn_opt_revision_t& rev);
};
#endif // REVISION_H
diff --git a/subversion/bindings/javahl/native/RevisionRange.cpp b/subversion/bindings/javahl/native/RevisionRange.cpp
index 677c7d5..d1c8c64 100644
--- a/subversion/bindings/javahl/native/RevisionRange.cpp
+++ b/subversion/bindings/javahl/native/RevisionRange.cpp
@@ -44,60 +44,117 @@ RevisionRange::~RevisionRange()
// explicitly destroyed.
}
-const svn_opt_revision_range_t *RevisionRange::toRange(SVN::Pool &pool) const
+namespace {
+void get_range_info(jobject jrange,
+ svn_opt_revision_t* range_start,
+ svn_opt_revision_t* range_end,
+ svn_boolean_t* range_inheritable)
{
JNIEnv *env = JNIUtil::getEnv();
- jclass clazz = env->FindClass(JAVA_PACKAGE"/types/RevisionRange");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/types/RevisionRange"));
if (JNIUtil::isExceptionThrown())
- return NULL;
+ return;
- static jmethodID fmid = 0;
- if (fmid == 0)
+ if (range_start)
{
- fmid = env->GetMethodID(clazz, "getFromRevision",
- "()L"JAVA_PACKAGE"/types/Revision;");
- if (JNIUtil::isJavaExceptionThrown())
- return NULL;
+
+ static jmethodID fmid = 0;
+ if (fmid == 0)
+ {
+ fmid = env->GetMethodID(clazz, "getFromRevision",
+ "()" JAVAHL_ARG("/types/Revision;"));
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ }
+
+ jobject jstartRevision = env->CallObjectMethod(jrange, fmid);
+ if (JNIUtil::isExceptionThrown())
+ return;
+
+ Revision startRevision(jstartRevision);
+ if (JNIUtil::isExceptionThrown())
+ return;
+
+ *range_start = *startRevision.revision();
+ if (JNIUtil::isExceptionThrown())
+ return;
}
- static jmethodID tmid = 0;
- if (tmid == 0)
+ if (range_end)
{
- tmid = env->GetMethodID(clazz, "getToRevision",
- "()L"JAVA_PACKAGE"/types/Revision;");
- if (JNIUtil::isJavaExceptionThrown())
- return NULL;
+ static jmethodID tmid = 0;
+ if (tmid == 0)
+ {
+ tmid = env->GetMethodID(clazz, "getToRevision",
+ "()" JAVAHL_ARG("/types/Revision;"));
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ }
+
+ jobject jendRevision = env->CallObjectMethod(jrange, tmid);
+ if (JNIUtil::isExceptionThrown())
+ return;
+
+ Revision endRevision(jendRevision);
+ if (JNIUtil::isExceptionThrown())
+ return;
+
+ *range_end = *endRevision.revision();
+ if (JNIUtil::isExceptionThrown())
+ return;
}
- jobject jstartRevision = env->CallObjectMethod(m_range, fmid);
- if (JNIUtil::isExceptionThrown())
- return NULL;
+ if (range_inheritable)
+ {
+ static jmethodID imid = 0;
+ if (imid == 0 && range_inheritable)
+ {
+ imid = env->GetMethodID(clazz, "isInheritable", "()Z");
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ }
+
+ jboolean inheritable = env->CallBooleanMethod(jrange, imid);
+ if (JNIUtil::isExceptionThrown())
+ return;
+ *range_inheritable = inheritable;
+ }
+}
+} // anonymous namespace
- Revision startRevision(jstartRevision);
+svn_merge_range_t* RevisionRange::toMergeRange(SVN::Pool &pool) const
+{
+ svn_opt_revision_t range_start, range_end;
+ svn_boolean_t range_inheritable;
+ get_range_info(m_range, &range_start, &range_end, &range_inheritable);
if (JNIUtil::isExceptionThrown())
return NULL;
- jobject jendRevision = env->CallObjectMethod(m_range, tmid);
- if (JNIUtil::isExceptionThrown())
- return NULL;
+ if (range_start.kind != svn_opt_revision_number
+ || range_end.kind != svn_opt_revision_number)
+ JNIUtil::raiseThrowable("java.lang.InvalidStateException",
+ "Revsision ranges must contain revision numbers");
- Revision endRevision(jendRevision);
- if (JNIUtil::isExceptionThrown())
- return NULL;
+ svn_merge_range_t* range =
+ static_cast<svn_merge_range_t*>
+ (apr_palloc(pool.getPool(), sizeof(*range)));
+
+ range->start = range_start.value.number;
+ range->end = range_end.value.number;
+ range->inheritable = range_inheritable;
+ return range;
+}
+svn_opt_revision_range_t *RevisionRange::toRange(SVN::Pool &pool) const
+{
svn_opt_revision_range_t *range =
- reinterpret_cast<svn_opt_revision_range_t *>
+ static_cast<svn_opt_revision_range_t *>
(apr_palloc(pool.getPool(), sizeof(*range)));
- range->start = *startRevision.revision();
- if (JNIUtil::isExceptionThrown())
- return NULL;
-
- range->end = *endRevision.revision();
+ get_range_info(m_range, &range->start, &range->end, NULL);
if (JNIUtil::isExceptionThrown())
- return NULL;
-
+ range = NULL;
return range;
}
@@ -106,19 +163,20 @@ RevisionRange::makeJRevisionRange(svn_merge_range_t *range)
{
JNIEnv *env = JNIUtil::getEnv();
- jclass rangeClazz = env->FindClass(JAVA_PACKAGE "/types/RevisionRange");
+ jclass rangeClazz = env->FindClass(JAVAHL_CLASS("/types/RevisionRange"));
if (JNIUtil::isJavaExceptionThrown())
return NULL;
static jmethodID rangeCtor = 0;
if (rangeCtor == 0)
{
- rangeCtor = env->GetMethodID(rangeClazz, "<init>", "(JJ)V");
+ rangeCtor = env->GetMethodID(rangeClazz, "<init>", "(JJZ)V");
if (JNIUtil::isJavaExceptionThrown())
return NULL;
}
jobject jrange = env->NewObject(rangeClazz, rangeCtor,
- (jlong) range->start,
- (jlong) range->end);
+ jlong(range->start),
+ jlong(range->end),
+ jboolean(range->inheritable));
if (JNIUtil::isJavaExceptionThrown())
return NULL;
diff --git a/subversion/bindings/javahl/native/RevisionRange.h b/subversion/bindings/javahl/native/RevisionRange.h
index f932c1b..9934894 100644
--- a/subversion/bindings/javahl/native/RevisionRange.h
+++ b/subversion/bindings/javahl/native/RevisionRange.h
@@ -29,6 +29,7 @@
#include <jni.h>
#include "svn_types.h"
+#include "svn_opt.h"
#include "Pool.h"
@@ -53,7 +54,12 @@ class RevisionRange
/**
* Return an svn_opt_revision_range_t.
*/
- const svn_opt_revision_range_t *toRange(SVN::Pool &pool) const;
+ svn_opt_revision_range_t *toRange(SVN::Pool &pool) const;
+
+ /**
+ * Return an svn_merge_range_t.
+ */
+ svn_merge_range_t* toMergeRange(SVN::Pool &pool) const;
/**
* Make a (single) RevisionRange Java object.
diff --git a/subversion/bindings/javahl/native/RevisionRangeList.cpp b/subversion/bindings/javahl/native/RevisionRangeList.cpp
new file mode 100644
index 0000000..3759e5f
--- /dev/null
+++ b/subversion/bindings/javahl/native/RevisionRangeList.cpp
@@ -0,0 +1,128 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file RevisionRangeList.cpp
+ * @brief Implementat of the class RevisionRangeList
+ */
+
+#include "JNIUtil.h"
+#include "Iterator.h"
+#include "RevisionRange.h"
+#include "RevisionRangeList.h"
+
+RevisionRangeList::RevisionRangeList(jobject jrangelist, SVN::Pool &pool)
+ : m_rangelist(NULL)
+{
+ if (!jrangelist)
+ return;
+
+ Iterator iter(jrangelist);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ m_rangelist = apr_array_make(pool.getPool(), 0, sizeof(svn_merge_range_t*));
+ while (iter.hasNext())
+ {
+ svn_merge_range_t* range = RevisionRange(iter.next()).toMergeRange(pool);
+ if (JNIUtil::isExceptionThrown())
+ return;
+ APR_ARRAY_PUSH(m_rangelist, svn_merge_range_t*) = range;
+ }
+}
+
+RevisionRangeList RevisionRangeList::create(jobject jthis, SVN::Pool &pool)
+{
+ jobject jrangelist = NULL;
+
+ if (jthis)
+ {
+ JNIEnv *env = JNIUtil::getEnv();
+
+ jmethodID mid = 0;
+ if (mid == 0)
+ {
+ jclass cls = env->FindClass(JAVAHL_CLASS("/types/RevisionRangeList"));
+ if (JNIUtil::isJavaExceptionThrown())
+ return RevisionRangeList(NULL, pool);
+
+ mid = env->GetMethodID(cls, "getRanges", "()Ljava/util/List;");
+ if (JNIUtil::isJavaExceptionThrown())
+ return RevisionRangeList(NULL, pool);
+ }
+
+ jrangelist = env->CallObjectMethod(jthis, mid);
+ if (JNIUtil::isJavaExceptionThrown())
+ return RevisionRangeList(NULL, pool);
+ }
+ return RevisionRangeList(jrangelist, pool);
+}
+
+jobject RevisionRangeList::toList() const
+{
+ JNIEnv *env = JNIUtil::getEnv();
+
+ // Create a local frame for our references
+ env->PushLocalFrame(LOCAL_FRAME_SIZE);
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+
+ jclass clazz = env->FindClass("java/util/ArrayList");
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+
+ static jmethodID init_mid = 0;
+ if (init_mid == 0)
+ {
+ init_mid = env->GetMethodID(clazz, "<init>", "()V");
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+ }
+
+ static jmethodID add_mid = 0;
+ if (add_mid == 0)
+ {
+ add_mid = env->GetMethodID(clazz, "add", "(Ljava/lang/Object;)Z");
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+ }
+
+ jobject jranges = env->NewObject(clazz, init_mid);
+
+ for (int i = 0; i < m_rangelist->nelts; ++i)
+ {
+ // Convert svn_merge_range_t *'s to Java RevisionRange objects.
+ svn_merge_range_t *range =
+ APR_ARRAY_IDX(m_rangelist, i, svn_merge_range_t *);
+
+ jobject jrange = RevisionRange::makeJRevisionRange(range);
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+
+ env->CallBooleanMethod(jranges, add_mid, jrange);
+ if (JNIUtil::isJavaExceptionThrown())
+ POP_AND_RETURN_NULL;
+
+ env->DeleteLocalRef(jrange);
+ }
+
+ return env->PopLocalFrame(jranges);
+}
diff --git a/subversion/bindings/javahl/native/JNIThreadData.h b/subversion/bindings/javahl/native/RevisionRangeList.h
index 55d4141..116ff0d 100644
--- a/subversion/bindings/javahl/native/JNIThreadData.h
+++ b/subversion/bindings/javahl/native/RevisionRangeList.h
@@ -20,58 +20,53 @@
* ====================================================================
* @endcopyright
*
- * @file JNIThreadData.h
- * @brief Interface of the class JNIThreadData
+ * @file RevisionRangeList.h
+ * @brief Interface of the class RevisionRangeList
*/
-#ifndef JNITHREADDATA_H
-#define JNITHREADDATA_H
+#ifndef REVISION_RANGE_LIST_H
+#define REVISION_RANGE_LIST_H
#include <jni.h>
-#include "JNIUtil.h"
+#include "svn_mergeinfo.h"
-struct apr_threadkey_t;
+#include "Pool.h"
/**
- * This class implements thread local storage for JNIUtil.
+ * A wrapper for svn_rangelist_t
*/
-class JNIThreadData
+class RevisionRangeList
{
public:
- static void del(void *);
- static JNIThreadData *getThreadData();
- static bool initThreadData();
- static void pushNewThreadData();
- static void popThreadData();
- JNIThreadData();
- ~JNIThreadData();
-
/**
- * The current JNI environment.
+ * Create a RevisionRangeList object from a Java list of revision ranges.
*/
- JNIEnv *m_env;
+ RevisionRangeList(jobject jrangelist, SVN::Pool &pool);
/**
- * Flag that a Java execption has been detected.
+ * Create a RevisionRangeList object from a Java RevisionRangeList.
*/
- bool m_exceptionThrown;
+ static RevisionRangeList create(jobject jthis, SVN::Pool &pool);
/**
- * A buffer used for formating messages.
+ * Wrap an svn_rangelist_t.
*/
- char m_formatBuffer[JNIUtil::formatBufferSize];
+ explicit RevisionRangeList(svn_rangelist_t* ranges)
+ : m_rangelist(ranges)
+ {}
- private:
/**
- * Pointer to previous thread information to enable reentrent
- * calls.
+ * Return an svn_rangelist_t.
*/
- JNIThreadData *m_previous;
+ const svn_rangelist_t* get() const { return m_rangelist; }
/**
- * The key to address this thread local storage.
+ * Make a Java list of reivison ranges.
*/
- static apr_threadkey_t *g_key;
+ jobject toList() const;
+
+ private:
+ svn_rangelist_t* m_rangelist;
};
-#endif // JNITHREADDATA_H
+#endif // REVISION_RANGE_LIST_H
diff --git a/subversion/bindings/javahl/native/SVNBase.cpp b/subversion/bindings/javahl/native/SVNBase.cpp
index bbabc11..5e20f93 100644
--- a/subversion/bindings/javahl/native/SVNBase.cpp
+++ b/subversion/bindings/javahl/native/SVNBase.cpp
@@ -97,3 +97,29 @@ inline void SVNBase::findCppAddrFieldID(jfieldID *fid, const char *className,
}
}
}
+
+jobject SVNBase::createCppBoundObject(const char *clazzName)
+{
+ JNIEnv *env = JNIUtil::getEnv();
+
+ // Create java session object
+ jclass clazz = env->FindClass(clazzName);
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+
+ static jmethodID ctor = 0;
+ if (ctor == 0)
+ {
+ ctor = env->GetMethodID(clazz, "<init>", "(J)V");
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+ }
+
+ jlong cppAddr = this->getCppAddr();
+
+ jobject jself = env->NewObject(clazz, ctor, cppAddr);
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+
+ return jself;
+}
diff --git a/subversion/bindings/javahl/native/SVNBase.h b/subversion/bindings/javahl/native/SVNBase.h
index f47e3c0..9b3d41a 100644
--- a/subversion/bindings/javahl/native/SVNBase.h
+++ b/subversion/bindings/javahl/native/SVNBase.h
@@ -82,6 +82,11 @@ class SVNBase
*/
void dispose(jobject jthis, jfieldID *fid, const char *className);
+ /**
+ * Instantiates java object attached to this base object
+ */
+ jobject createCppBoundObject(const char *clazzName);
+
private:
/**
* If the value pointed to by @a fid is zero, find the @c jfieldID
diff --git a/subversion/bindings/javahl/native/SVNClient.cpp b/subversion/bindings/javahl/native/SVNClient.cpp
index 4945cc7..5bae852 100644
--- a/subversion/bindings/javahl/native/SVNClient.cpp
+++ b/subversion/bindings/javahl/native/SVNClient.cpp
@@ -24,12 +24,18 @@
* @brief: Implementation of the SVNClient class
*/
+#include <vector>
+#include <iostream>
+#include <sstream>
+#include <string>
+
#include "SVNClient.h"
#include "JNIUtil.h"
#include "CopySources.h"
#include "DiffSummaryReceiver.h"
#include "ClientContext.h"
#include "Prompter.h"
+#include "RemoteSession.h"
#include "Pool.h"
#include "Targets.h"
#include "Revision.h"
@@ -50,9 +56,11 @@
#include "CommitMessage.h"
#include "EnumMapper.h"
#include "StringArray.h"
-#include "RevpropTable.h"
+#include "PropertyTable.h"
#include "DiffOptions.h"
#include "CreateJ.h"
+#include "JNIStringHolder.h"
+
#include "svn_auth.h"
#include "svn_dso.h"
#include "svn_types.h"
@@ -62,14 +70,17 @@
#include "svn_diff.h"
#include "svn_config.h"
#include "svn_io.h"
+#include "svn_hash.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_utf.h"
+#include "private/svn_subr_private.h"
#include "svn_private_config.h"
-#include "JNIStringHolder.h"
-#include <vector>
-#include <iostream>
-#include <sstream>
+
+#include "ExternalItem.hpp"
+#include "jniwrapper/jni_list.hpp"
+#include "jniwrapper/jni_stack.hpp"
+#include "jniwrapper/jni_string_map.hpp"
SVNClient::SVNClient(jobject jthis_in)
@@ -85,21 +96,21 @@ SVNClient *SVNClient::getCppObject(jobject jthis)
{
static jfieldID fid = 0;
jlong cppAddr = SVNBase::findCppAddrForJObject(jthis, &fid,
- JAVA_PACKAGE"/SVNClient");
+ JAVAHL_CLASS("/SVNClient"));
return (cppAddr == 0 ? NULL : reinterpret_cast<SVNClient *>(cppAddr));
}
void SVNClient::dispose(jobject jthis)
{
static jfieldID fid = 0;
- SVNBase::dispose(jthis, &fid, JAVA_PACKAGE"/SVNClient");
+ SVNBase::dispose(jthis, &fid, JAVAHL_CLASS("/SVNClient"));
}
jobject SVNClient::getVersionExtended(bool verbose)
{
JNIEnv *const env = JNIUtil::getEnv();
- jclass clazz = env->FindClass(JAVA_PACKAGE"/types/VersionExtended");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/types/VersionExtended"));
if (JNIUtil::isJavaExceptionThrown())
return NULL;
@@ -184,8 +195,9 @@ void SVNClient::list(const char *url, Revision &revision,
void
SVNClient::status(const char *path, svn_depth_t depth,
- bool onServer, bool getAll, bool noIgnore,
- bool ignoreExternals, StringArray &changelists,
+ bool onServer, bool onDisk, bool getAll,
+ bool noIgnore, bool ignoreExternals,
+ bool depthAsSticky, StringArray &changelists,
StatusCallback *callback)
{
SVN::Pool subPool(pool);
@@ -204,11 +216,10 @@ SVNClient::status(const char *path, svn_depth_t depth,
rev.kind = svn_opt_revision_unspecified;
- SVN_JNI_ERR(svn_client_status5(&youngest, ctx, checkedPath.c_str(),
- &rev,
- depth,
- getAll, onServer, noIgnore, ignoreExternals,
- FALSE,
+ SVN_JNI_ERR(svn_client_status6(&youngest, ctx, checkedPath.c_str(),
+ &rev, depth,
+ getAll, onServer, onDisk,
+ noIgnore, ignoreExternals, depthAsSticky,
changelists.array(subPool),
StatusCallback::callback, callback,
subPool.getPool()), );
@@ -227,23 +238,23 @@ rev_range_vector_to_apr_array(std::vector<RevisionRange> &revRanges,
std::vector<RevisionRange>::const_iterator it;
for (it = revRanges.begin(); it != revRanges.end(); ++it)
{
- if (it->toRange(subPool)->start.kind
- == svn_opt_revision_unspecified
- && it->toRange(subPool)->end.kind
- == svn_opt_revision_unspecified)
+ const svn_opt_revision_range_t *range = it->toRange(subPool);
+
+ if (range->start.kind == svn_opt_revision_unspecified
+ && range->end.kind == svn_opt_revision_unspecified)
{
- svn_opt_revision_range_t *range =
+ svn_opt_revision_range_t *full =
reinterpret_cast<svn_opt_revision_range_t *>
(apr_pcalloc(subPool.getPool(), sizeof(*range)));
- range->start.kind = svn_opt_revision_number;
- range->start.value.number = 1;
- range->end.kind = svn_opt_revision_head;
- APR_ARRAY_PUSH(ranges, const svn_opt_revision_range_t *) = range;
+ full->start.kind = svn_opt_revision_number;
+ full->start.value.number = 1;
+ full->end.kind = svn_opt_revision_head;
+ full->end.value.number = 0;
+ APR_ARRAY_PUSH(ranges, const svn_opt_revision_range_t *) = full;
}
else
{
- APR_ARRAY_PUSH(ranges, const svn_opt_revision_range_t *) =
- it->toRange(subPool);
+ APR_ARRAY_PUSH(ranges, const svn_opt_revision_range_t *) = range;
}
if (JNIUtil::isExceptionThrown())
return NULL;
@@ -255,7 +266,7 @@ void SVNClient::logMessages(const char *path, Revision &pegRevision,
std::vector<RevisionRange> &logRanges,
bool stopOnCopy, bool discoverPaths,
bool includeMergedRevisions, StringArray &revProps,
- long limit, LogMessageCallback *callback)
+ int limit, LogMessageCallback *callback)
{
SVN::Pool subPool(pool);
@@ -317,7 +328,7 @@ jlong SVNClient::checkout(const char *moduleName, const char *destPath,
}
void SVNClient::remove(Targets &targets, CommitMessage *message, bool force,
- bool keep_local, RevpropTable &revprops,
+ bool keep_local, PropertyTable &revprops,
CommitCallback *callback)
{
SVN::Pool subPool(pool);
@@ -334,23 +345,24 @@ void SVNClient::remove(Targets &targets, CommitMessage *message, bool force,
ctx, subPool.getPool()), );
}
-void SVNClient::revert(const char *path, svn_depth_t depth,
- StringArray &changelists)
+void SVNClient::revert(StringArray &paths, svn_depth_t depth,
+ StringArray &changelists,
+ bool clear_changelists,
+ bool metadata_only)
{
SVN::Pool subPool(pool);
- SVN_JNI_NULL_PTR_EX(path, "path", );
-
svn_client_ctx_t *ctx = context.getContext(NULL, subPool);
if (ctx == NULL)
return;
- Targets target(path, subPool);
- const apr_array_header_t *targets = target.array(subPool);
- SVN_JNI_ERR(target.error_occurred(), );
- SVN_JNI_ERR(svn_client_revert2(targets, depth,
- changelists.array(subPool), ctx,
- subPool.getPool()), );
+ Targets targets(paths, subPool);
+ SVN_JNI_ERR(targets.error_occurred(), );
+ SVN_JNI_ERR(svn_client_revert3(targets.array(subPool), depth,
+ changelists.array(subPool),
+ clear_changelists,
+ metadata_only,
+ ctx, subPool.getPool()), );
}
void SVNClient::add(const char *path,
@@ -417,7 +429,7 @@ jlongArray SVNClient::update(Targets &targets, Revision &revision,
void SVNClient::commit(Targets &targets, CommitMessage *message,
svn_depth_t depth, bool noUnlock, bool keepChangelist,
- StringArray &changelists, RevpropTable &revprops,
+ StringArray &changelists, PropertyTable &revprops,
CommitCallback *callback)
{
SVN::Pool subPool(pool);
@@ -439,20 +451,82 @@ void SVNClient::commit(Targets &targets, CommitMessage *message,
);
}
+
+namespace {
+typedef Java::ImmutableList<JavaHL::ExternalItem> PinList;
+typedef Java::ImmutableMap<PinList> PinMap;
+
+struct PinListFunctor
+{
+ explicit PinListFunctor(const Java::Env& env, SVN::Pool& pool, int refs_len)
+ : m_pool(pool),
+ m_refs(apr_array_make(pool.getPool(), refs_len,
+ sizeof(svn_wc_external_item2_t*)))
+ {}
+
+ void operator()(const JavaHL::ExternalItem& item)
+ {
+ APR_ARRAY_PUSH(m_refs, svn_wc_external_item2_t*) =
+ item.get_external_item(m_pool);
+ }
+
+ SVN::Pool& m_pool;
+ apr_array_header_t *m_refs;
+};
+
+struct PinMapFunctor
+{
+ explicit PinMapFunctor(const Java::Env& env, SVN::Pool& pool)
+ : m_env(env),
+ m_pool(pool),
+ m_pin_set(svn_hash__make(pool.getPool()))
+ {}
+
+ void operator()(const std::string& path, const PinList& refs)
+ {
+ PinListFunctor lf(m_env, m_pool, refs.length());
+ refs.for_each(lf);
+ const char* key = static_cast<const char*>(
+ apr_pmemdup(m_pool.getPool(), path.c_str(), path.size() + 1));
+ svn_hash_sets(m_pin_set, key, lf.m_refs);
+ }
+
+ const Java::Env& m_env;
+ SVN::Pool& m_pool;
+ apr_hash_t *m_pin_set;
+};
+
+apr_hash_t *get_externals_to_pin(jobject jexternalsToPin, SVN::Pool& pool)
+{
+ if (!jexternalsToPin)
+ return NULL;
+
+ const Java::Env env;
+ JNIEnv *jenv = env.get();
+
+ try
+ {
+ PinMap pin_map(env, jexternalsToPin);
+ PinMapFunctor mf(env, pool);
+ pin_map.for_each(mf);
+ return mf.m_pin_set;
+ }
+ SVN_JAVAHL_JNI_CATCH;
+ return NULL;
+}
+} // anonymous namespace
+
void SVNClient::copy(CopySources &copySources, const char *destPath,
CommitMessage *message, bool copyAsChild,
bool makeParents, bool ignoreExternals,
- RevpropTable &revprops, CommitCallback *callback)
+ bool metadataOnly,
+ bool pinExternals, jobject jexternalsToPin,
+ PropertyTable &revprops, CommitCallback *callback)
{
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(), );
@@ -461,17 +535,21 @@ void SVNClient::copy(CopySources &copySources, const char *destPath,
if (ctx == NULL)
return;
- SVN_JNI_ERR(svn_client_copy6(srcs, destinationPath.c_str(),
- copyAsChild, makeParents, ignoreExternals,
- revprops.hash(subPool),
- CommitCallback::callback, callback,
+ apr_hash_t *pin_set = get_externals_to_pin(jexternalsToPin, subPool);
+ if (!JNIUtil::isJavaExceptionThrown())
+ SVN_JNI_ERR(svn_client_copy7(srcs, destinationPath.c_str(),
+ copyAsChild, makeParents, ignoreExternals,
+ metadataOnly,
+ pinExternals, pin_set,
+ revprops.hash(subPool),
+ CommitCallback::callback, callback,
ctx, subPool.getPool()), );
}
void SVNClient::move(Targets &srcPaths, const char *destPath,
CommitMessage *message, bool force, bool moveAsChild,
bool makeParents, bool metadataOnly, bool allowMixRev,
- RevpropTable &revprops, CommitCallback *callback)
+ PropertyTable &revprops, CommitCallback *callback)
{
SVN::Pool subPool(pool);
@@ -496,7 +574,7 @@ void SVNClient::move(Targets &srcPaths, const char *destPath,
}
void SVNClient::mkdir(Targets &targets, CommitMessage *message,
- bool makeParents, RevpropTable &revprops,
+ bool makeParents, PropertyTable &revprops,
CommitCallback *callback)
{
SVN::Pool subPool(pool);
@@ -513,7 +591,12 @@ void SVNClient::mkdir(Targets &targets, CommitMessage *message,
ctx, subPool.getPool()), );
}
-void SVNClient::cleanup(const char *path)
+void SVNClient::cleanup(const char *path,
+ bool break_locks,
+ bool fix_recorded_timestamps,
+ bool clear_dav_cache,
+ bool remove_unused_pristines,
+ bool include_externals)
{
SVN::Pool subPool(pool);
SVN_JNI_NULL_PTR_EX(path, "path", );
@@ -524,7 +607,13 @@ void SVNClient::cleanup(const char *path)
if (ctx == NULL)
return;
- SVN_JNI_ERR(svn_client_cleanup(intPath.c_str(), ctx, subPool.getPool()),);
+ SVN_JNI_ERR(svn_client_cleanup2(intPath.c_str(),
+ break_locks,
+ fix_recorded_timestamps,
+ clear_dav_cache,
+ remove_unused_pristines,
+ include_externals,
+ ctx, subPool.getPool()),);
}
void SVNClient::resolve(const char *path, svn_depth_t depth,
@@ -545,6 +634,7 @@ void SVNClient::resolve(const char *path, svn_depth_t depth,
jlong SVNClient::doExport(const char *srcPath, const char *destPath,
Revision &revision, Revision &pegRevision,
bool force, bool ignoreExternals,
+ bool ignoreKeywords,
svn_depth_t depth, const char *nativeEOL)
{
SVN::Pool subPool(pool);
@@ -563,7 +653,7 @@ jlong SVNClient::doExport(const char *srcPath, const char *destPath,
destinationPath.c_str(),
pegRevision.revision(),
revision.revision(), force,
- ignoreExternals, FALSE,
+ ignoreExternals, ignoreKeywords,
depth,
nativeEOL, ctx,
subPool.getPool()),
@@ -613,7 +703,7 @@ void SVNClient::doImport(const char *path, const char *url,
CommitMessage *message, svn_depth_t depth,
bool noIgnore, bool noAutoProps,
bool ignoreUnknownNodeTypes,
- RevpropTable &revprops,
+ PropertyTable &revprops,
ImportFilterCallback *ifCallback,
CommitCallback *commitCallback)
{
@@ -658,7 +748,7 @@ void SVNClient::merge(const char *path1, Revision &revision1,
const char *path2, Revision &revision2,
const char *localPath, bool forceDelete, svn_depth_t depth,
bool ignoreMergeinfo, bool diffIgnoreAncestry,
- bool dryRun, bool recordOnly)
+ bool dryRun, bool allowMixedRev, bool recordOnly)
{
SVN::Pool subPool(pool);
SVN_JNI_NULL_PTR_EX(path1, "path1", );
@@ -683,14 +773,15 @@ void SVNClient::merge(const char *path1, Revision &revision1,
depth,
ignoreMergeinfo, diffIgnoreAncestry,
forceDelete, recordOnly, dryRun,
- TRUE, NULL, ctx, subPool.getPool()), );
+ allowMixedRev, NULL, ctx,
+ subPool.getPool()), );
}
void SVNClient::merge(const char *path, Revision &pegRevision,
std::vector<RevisionRange> *rangesToMerge,
const char *localPath, bool forceDelete, svn_depth_t depth,
bool ignoreMergeinfo, bool diffIgnoreAncestry,
- bool dryRun, bool recordOnly)
+ bool dryRun, bool allowMixedRev, bool recordOnly)
{
SVN::Pool subPool(pool);
SVN_JNI_NULL_PTR_EX(path, "path", );
@@ -718,39 +809,16 @@ void SVNClient::merge(const char *path, Revision &pegRevision,
depth,
ignoreMergeinfo, diffIgnoreAncestry,
forceDelete, recordOnly,
- dryRun, TRUE, NULL, ctx,
+ dryRun, allowMixedRev, NULL, ctx,
subPool.getPool()), );
}
-void SVNClient::mergeReintegrate(const char *path, Revision &pegRevision,
- const char *localPath, bool dryRun)
-{
- SVN::Pool subPool(pool);
- SVN_JNI_NULL_PTR_EX(path, "path", );
- SVN_JNI_NULL_PTR_EX(localPath, "localPath", );
- Path intLocalPath(localPath, subPool);
- SVN_JNI_ERR(intLocalPath.error_occurred(), );
-
- Path srcPath(path, subPool);
- SVN_JNI_ERR(srcPath.error_occurred(), );
-
- svn_client_ctx_t *ctx = context.getContext(NULL, subPool);
- if (ctx == NULL)
- return;
-
- SVN_JNI_ERR(svn_client_merge_reintegrate(srcPath.c_str(),
- pegRevision.revision(),
- intLocalPath.c_str(),
- dryRun, NULL, ctx,
- subPool.getPool()), );
-}
+/* SVNClient::mergeReintegrate is implemented in deprecated.cpp. */
jobject
SVNClient::getMergeinfo(const char *target, Revision &pegRevision)
{
SVN::Pool subPool(pool);
- JNIEnv *env = JNIUtil::getEnv();
-
svn_client_ctx_t *ctx = context.getContext(NULL, subPool);
if (ctx == NULL)
return NULL;
@@ -765,57 +833,7 @@ SVNClient::getMergeinfo(const char *target, Revision &pegRevision)
NULL);
if (mergeinfo == NULL)
return NULL;
-
- // Transform mergeinfo into Java Mergeinfo object.
- jclass clazz = env->FindClass(JAVA_PACKAGE "/types/Mergeinfo");
- if (JNIUtil::isJavaExceptionThrown())
- return NULL;
-
- static jmethodID ctor = 0;
- if (ctor == 0)
- {
- ctor = env->GetMethodID(clazz, "<init>", "()V");
- if (JNIUtil::isJavaExceptionThrown())
- return NULL;
- }
-
- static jmethodID addRevisions = 0;
- if (addRevisions == 0)
- {
- addRevisions = env->GetMethodID(clazz, "addRevisions",
- "(Ljava/lang/String;"
- "Ljava/util/List;)V");
- if (JNIUtil::isJavaExceptionThrown())
- return NULL;
- }
-
- jobject jmergeinfo = env->NewObject(clazz, ctor);
- if (JNIUtil::isJavaExceptionThrown())
- return NULL;
-
- apr_hash_index_t *hi;
- for (hi = apr_hash_first(subPool.getPool(), mergeinfo);
- hi;
- hi = apr_hash_next(hi))
- {
- const void *path;
- void *val;
- apr_hash_this(hi, &path, NULL, &val);
-
- jstring jpath =
- JNIUtil::makeJString(reinterpret_cast<const char *>(path));
- jobject jranges =
- CreateJ::RevisionRangeList(reinterpret_cast<svn_rangelist_t *>(val));
-
- env->CallVoidMethod(jmergeinfo, addRevisions, jpath, jranges);
-
- env->DeleteLocalRef(jranges);
- env->DeleteLocalRef(jpath);
- }
-
- env->DeleteLocalRef(clazz);
-
- return jmergeinfo;
+ return CreateJ::Mergeinfo(mergeinfo, subPool.getPool());
}
void SVNClient::getMergeinfoLog(int type, const char *pathOrURL,
@@ -954,7 +972,7 @@ void SVNClient::propertySetRemote(const char *path, long base_rev,
const char *name,
CommitMessage *message,
JNIByteArray &value, bool force,
- RevpropTable &revprops,
+ PropertyTable &revprops,
CommitCallback *callback)
{
SVN::Pool subPool(pool);
@@ -1155,23 +1173,31 @@ SVNClient::diffSummarize(const char *target, Revision &pegRevision,
subPool.getPool()), );
}
-void SVNClient::streamFileContent(const char *path, Revision &revision,
- Revision &pegRevision,
- OutputStream &outputStream)
+apr_hash_t *SVNClient::streamFileContent(const char *path,
+ Revision &revision,
+ Revision &pegRevision,
+ bool expand_keywords,
+ bool return_props,
+ OutputStream &outputStream)
{
SVN::Pool subPool(pool);
- SVN_JNI_NULL_PTR_EX(path, "path", );
+ SVN_JNI_NULL_PTR_EX(path, "path", NULL);
Path intPath(path, subPool);
- SVN_JNI_ERR(intPath.error_occurred(), );
+ SVN_JNI_ERR(intPath.error_occurred(), NULL);
svn_client_ctx_t *ctx = context.getContext(NULL, subPool);
if (ctx == NULL)
- return;
+ return NULL;
- SVN_JNI_ERR(svn_client_cat2(outputStream.getStream(subPool),
- intPath.c_str(), pegRevision.revision(),
- revision.revision(), ctx, subPool.getPool()),
- );
+ apr_hash_t *props = NULL;
+ SVN_JNI_ERR(svn_client_cat3((return_props ? &props : NULL),
+ outputStream.getStream(subPool),
+ intPath.c_str(),
+ pegRevision.revision(), revision.revision(),
+ expand_keywords, ctx,
+ subPool.getPool(), subPool.getPool()),
+ NULL);
+ return props;
}
jbyteArray SVNClient::revProperty(const char *path,
@@ -1239,7 +1265,7 @@ void SVNClient::relocate(const char *from, const char *to, const char *path,
void SVNClient::blame(const char *path, Revision &pegRevision,
Revision &revisionStart, Revision &revisionEnd,
bool ignoreMimeType, bool includeMergedRevisions,
- BlameCallback *callback)
+ BlameCallback *callback, DiffOptions const& options)
{
SVN::Pool subPool(pool);
SVN_JNI_NULL_PTR_EX(path, "path", );
@@ -1253,7 +1279,7 @@ void SVNClient::blame(const char *path, Revision &pegRevision,
SVN_JNI_ERR(svn_client_blame5(
intPath.c_str(), pegRevision.revision(), revisionStart.revision(),
revisionEnd.revision(),
- svn_diff_file_options_create(subPool.getPool()), ignoreMimeType,
+ options.fileOptions(subPool), ignoreMimeType,
includeMergedRevisions, BlameCallback::callback, callback, ctx,
subPool.getPool()),
);
@@ -1288,15 +1314,17 @@ void SVNClient::removeFromChangelists(Targets &srcPaths, svn_depth_t depth,
}
void SVNClient::getChangelists(const char *rootPath,
- StringArray &changelists,
+ StringArray *changelists,
svn_depth_t depth,
ChangelistCallback *callback)
{
SVN::Pool subPool(pool);
svn_client_ctx_t *ctx = context.getContext(NULL, subPool);
- SVN_JNI_ERR(svn_client_get_changelists(rootPath,
- changelists.array(subPool),
+ const apr_array_header_t *cl_array = (!changelists ? NULL
+ : changelists->array(subPool));
+
+ SVN_JNI_ERR(svn_client_get_changelists(rootPath, cl_array,
depth, ChangelistCallback::callback,
callback, ctx, subPool.getPool()),
);
@@ -1392,10 +1420,10 @@ jstring SVNClient::getVersionInfo(const char *path, const char *trailUrl,
}
else
{
- char *message = JNIUtil::getFormatBuffer();
- apr_snprintf(message, JNIUtil::formatBufferSize,
+ char buffer[2048];
+ apr_snprintf(buffer, sizeof(buffer),
_("'%s' not versioned, and not exported\n"), path);
- return JNIUtil::makeJString(message);
+ return JNIUtil::makeJString(buffer);
}
}
@@ -1465,19 +1493,15 @@ jobject SVNClient::revProperties(const char *path, Revision &revision)
&set_rev, ctx, subPool.getPool()),
NULL);
- return CreateJ::PropertyMap(props);
+ return CreateJ::PropertyMap(props, subPool.getPool());
}
-struct info_baton
-{
- std::vector<info_entry> infoVect;
- apr_pool_t *pool;
-};
-
void
-SVNClient::info2(const char *path, Revision &revision, Revision &pegRevision,
- svn_depth_t depth, StringArray &changelists,
- InfoCallback *callback)
+SVNClient::info(const char *path,
+ Revision &revision, Revision &pegRevision, svn_depth_t depth,
+ svn_boolean_t fetchExcluded, svn_boolean_t fetchActualOnly,
+ svn_boolean_t includeExternals,
+ StringArray &changelists, InfoCallback *callback)
{
SVN_JNI_NULL_PTR_EX(path, "path", );
@@ -1489,10 +1513,11 @@ SVNClient::info2(const char *path, Revision &revision, Revision &pegRevision,
Path checkedPath(path, subPool);
SVN_JNI_ERR(checkedPath.error_occurred(), );
- SVN_JNI_ERR(svn_client_info3(checkedPath.c_str(),
+ SVN_JNI_ERR(svn_client_info4(checkedPath.c_str(),
pegRevision.revision(),
- revision.revision(),
- depth, FALSE, TRUE,
+ revision.revision(), depth,
+ fetchExcluded, fetchActualOnly,
+ includeExternals,
changelists.array(subPool),
InfoCallback::callback, callback,
ctx, subPool.getPool()), );
@@ -1525,6 +1550,87 @@ SVNClient::patch(const char *patchPath, const char *targetPath, bool dryRun,
ctx, subPool.getPool()), );
}
+void SVNClient::vacuum(const char *path,
+ bool remove_unversioned_items,
+ bool remove_ignored_items,
+ bool fix_recorded_timestamps,
+ bool remove_unused_pristines,
+ bool include_externals)
+{
+ SVN_JNI_NULL_PTR_EX(path, "path", );
+
+ SVN::Pool subPool(pool);
+ svn_client_ctx_t *ctx = context.getContext(NULL, subPool);
+ if (ctx == NULL)
+ return;
+
+ Path checkedPath(path, subPool);
+ SVN_JNI_ERR(checkedPath.error_occurred(),);
+
+ SVN_JNI_ERR(svn_client_vacuum(checkedPath.c_str(),
+ remove_unversioned_items,
+ remove_ignored_items,
+ fix_recorded_timestamps,
+ remove_unused_pristines,
+ include_externals,
+ ctx, subPool.getPool()), );
+}
+
+jobject
+SVNClient::openRemoteSession(const char* path, int retryAttempts)
+{
+ static const svn_opt_revision_t HEAD = { svn_opt_revision_head, {0}};
+ static const svn_opt_revision_t NONE = { svn_opt_revision_unspecified, {0}};
+
+ SVN_JNI_NULL_PTR_EX(path, "path", NULL);
+
+ SVN::Pool subPool(pool);
+ svn_client_ctx_t *ctx = context.getContext(NULL, subPool);
+ if (ctx == NULL)
+ return NULL;
+
+ Path checkedPath(path, subPool);
+ SVN_JNI_ERR(checkedPath.error_occurred(), NULL);
+
+ struct PathInfo
+ {
+ std::string url;
+ std::string uuid;
+ static svn_error_t *callback(void *baton,
+ const char *,
+ const svn_client_info2_t *info,
+ apr_pool_t *)
+ {
+ PathInfo* const pi = static_cast<PathInfo*>(baton);
+ pi->url = info->URL;
+ pi->uuid = info->repos_UUID;
+ return SVN_NO_ERROR;
+ }
+ } path_info;
+
+ SVN_JNI_ERR(svn_client_info4(
+ checkedPath.c_str(), &NONE,
+ (svn_path_is_url(checkedPath.c_str()) ? &HEAD : &NONE),
+ svn_depth_empty, FALSE, TRUE, FALSE, NULL,
+ PathInfo::callback, &path_info,
+ ctx, subPool.getPool()),
+ NULL);
+
+ /* Decouple the RemoteSession's context from SVNClient's context
+ by creating a copy of the prompter here. */
+
+ jobject jremoteSession = RemoteSession::open(
+ retryAttempts, path_info.url.c_str(), path_info.uuid.c_str(),
+ context.getConfigDirectory(),
+ context.getUsername(), context.getPassword(),
+ context.clonePrompter(), context.getSelf(),
+ context.getConfigEventHandler(), context.getTunnelCallback());
+ if (JNIUtil::isJavaExceptionThrown())
+ jremoteSession = NULL;
+
+ return jremoteSession;
+}
+
ClientContext &
SVNClient::getClientContext()
{
diff --git a/subversion/bindings/javahl/native/SVNClient.h b/subversion/bindings/javahl/native/SVNClient.h
index 8d85d9b..6661bae 100644
--- a/subversion/bindings/javahl/native/SVNClient.h
+++ b/subversion/bindings/javahl/native/SVNClient.h
@@ -53,7 +53,7 @@ class PatchCallback;
class ChangelistCallback;
class CommitMessage;
class StringArray;
-class RevpropTable;
+class PropertyTable;
class DiffOptions;
#include "svn_types.h"
#include "svn_client.h"
@@ -62,12 +62,19 @@ class DiffOptions;
class SVNClient :public SVNBase
{
public:
+ jobject openRemoteSession(const char* path, int);
+ void vacuum(const char *path,
+ bool remove_unversioned_items, bool remove_ignored_items,
+ bool fix_recorded_timestamps, bool remove_unused_pristines,
+ bool include_externals);
void patch(const char *patchPath, const char *targetPath, bool dryRun,
int stripCount, bool reverse, bool ignoreWhitespace,
bool removeTempfiles, PatchCallback *callback);
- void info2(const char *path, Revision &revision, Revision &pegRevision,
- svn_depth_t depth, StringArray &changelists,
- InfoCallback *callback);
+ void info(const char *path,
+ Revision &revision, Revision &pegRevision, svn_depth_t depth,
+ svn_boolean_t fetchExcluded, svn_boolean_t fetchActualOnly,
+ svn_boolean_t includeExternals,
+ StringArray &changelists, InfoCallback *callback);
void unlock(Targets &targets, bool force);
void lock(Targets &targets, const char *comment, bool force);
jobject revProperties(const char *path, Revision &revision);
@@ -75,18 +82,20 @@ class SVNClient :public SVNBase
void blame(const char *path, Revision &pegRevision,
Revision &revisionStart, Revision &revisionEnd,
bool ignoreMimeType, bool includeMergedRevisions,
- BlameCallback *callback);
+ BlameCallback *callback, DiffOptions const& options);
void relocate(const char *from, const char *to, const char *path,
bool ignoreExternals);
- void streamFileContent(const char *path, Revision &revision,
- Revision &pegRevision, OutputStream &outputStream);
+ apr_hash_t *streamFileContent(const char *path,
+ Revision &revision, Revision &pegRevision,
+ bool expand_keywords, bool return_props,
+ OutputStream &outputStream);
void propertySetLocal(Targets &targets, const char *name, JNIByteArray &value,
svn_depth_t depth, StringArray &changelists,
bool force);
void propertySetRemote(const char *path, long base_rev, const char *name,
CommitMessage *message,
JNIByteArray &value, bool force,
- RevpropTable &revprops, CommitCallback *callback);
+ PropertyTable &revprops, CommitCallback *callback);
void properties(const char *path, Revision &revision,
Revision &pegRevision, svn_depth_t depth,
StringArray &changelists, ProplistCallback *callback);
@@ -103,18 +112,18 @@ class SVNClient :public SVNBase
const char *path2, Revision &revision2,
const char *localPath, bool forceDelete, svn_depth_t depth,
bool ignoreMergeinfo, bool diffIgnoreAncestry,
- bool dryRun, bool recordOnly);
+ bool dryRun, bool allowMixedRev, bool recordOnly);
void merge(const char *path, Revision &pegRevision,
std::vector<RevisionRange> *rangesToMerge,
const char *localPath, bool forceDelete, svn_depth_t depth,
bool ignoreMergeinfo, bool diffIgnoreAncestry,
- bool dryRun, bool recordOnly);
+ bool dryRun, bool allowMixedRev, bool recordOnly);
void mergeReintegrate(const char *path, Revision &pegRevision,
const char *localPath, bool dryRun);
void doImport(const char *path, const char *url, CommitMessage *message,
svn_depth_t depth, bool noIgnore, bool noAutoProps,
bool ignoreUnknownNodeTypes,
- RevpropTable &revprops, ImportFilterCallback *ifCallback,
+ PropertyTable &revprops, ImportFilterCallback *ifCallback,
CommitCallback *commitCallback);
jlong doSwitch(const char *path, const char *url, Revision &revision,
Revision &pegRevision, svn_depth_t depth,
@@ -122,33 +131,40 @@ class SVNClient :public SVNBase
bool allowUnverObstructions, bool ignoreAncestry);
jlong doExport(const char *srcPath, const char *destPath,
Revision &revision, Revision &pegRevision, bool force,
- bool ignoreExternals, svn_depth_t depth,
- const char *nativeEOL);
+ bool ignoreExternals, bool ignoreKeywords,
+ svn_depth_t depth, const char *nativeEOL);
void resolve(const char *path, svn_depth_t depth,
svn_wc_conflict_choice_t choice);
- void cleanup(const char *path);
+ void cleanup(const char *path,
+ bool break_locks,
+ bool fix_recorded_timestamps,
+ bool clear_dav_cache,
+ bool remove_unused_pristines,
+ bool include_externals);
void mkdir(Targets &targets, CommitMessage *message, bool makeParents,
- RevpropTable &revprops, CommitCallback *callback);
+ PropertyTable &revprops, CommitCallback *callback);
void move(Targets &srcPaths, const char *destPath,
CommitMessage *message, bool force, bool moveAsChild,
bool makeParents, bool metadataOnly, bool allowMixRev,
- RevpropTable &revprops, CommitCallback *callback);
+ PropertyTable &revprops, CommitCallback *callback);
void copy(CopySources &copySources, const char *destPath,
CommitMessage *message, bool copyAsChild, bool makeParents,
- bool ignoreExternals, RevpropTable &revprops,
- CommitCallback *callback);
+ bool ignoreExternals, bool metaDataOnly, bool pinExternals,
+ jobject jexternalsToPin,
+ PropertyTable &revprops, CommitCallback *callback);
void commit(Targets &targets, CommitMessage *message, svn_depth_t depth,
bool noUnlock, bool keepChangelist,
- StringArray &changelists, RevpropTable &revprops,
+ StringArray &changelists, PropertyTable &revprops,
CommitCallback *callback);
jlongArray update(Targets &targets, Revision &revision, svn_depth_t depth,
bool depthIsSticky, bool makeParents, bool ignoreExternals,
bool allowUnverObstructions);
void add(const char *path, svn_depth_t depth, bool force,
bool no_ignore, bool no_autoprops, bool add_parents);
- void revert(const char *path, svn_depth_t depth, StringArray &changelists);
+ void revert(StringArray &paths, svn_depth_t depth, StringArray &changelists,
+ bool clear_changelists, bool metadata_only);
void remove(Targets &targets, CommitMessage *message, bool force,
- bool keep_local, RevpropTable &revprops,
+ bool keep_local, PropertyTable &revprops,
CommitCallback *callback);
jlong checkout(const char *moduleName, const char *destPath,
Revision &revision, Revision &pegRevsion, svn_depth_t depth,
@@ -157,7 +173,7 @@ class SVNClient :public SVNBase
std::vector<RevisionRange> &ranges, bool stopOnCopy,
bool discoverPaths, bool includeMergedRevisions,
StringArray &revProps,
- long limit, LogMessageCallback *callback);
+ int limit, LogMessageCallback *callback);
jobject getVersionExtended(bool verbose);
jstring getAdminDirectoryName();
jboolean isAdminDirectory(const char *name);
@@ -165,11 +181,13 @@ class SVNClient :public SVNBase
svn_depth_t depth, StringArray &changelists);
void removeFromChangelists(Targets &srcPaths, svn_depth_t depth,
StringArray &changelists);
- void getChangelists(const char *path, StringArray &changelists,
+ void getChangelists(const char *path, StringArray *changelists,
svn_depth_t depth, ChangelistCallback *callback);
- void status(const char *path, svn_depth_t depth, bool onServer,
- bool getAll, bool noIgnore, bool ignoreExternals,
- StringArray &changelists, StatusCallback *callback);
+ void status(const char *path, svn_depth_t depth,
+ bool onServer, bool onDisk, bool getAll,
+ bool noIgnore, bool ignoreExternals,
+ bool depthAsSticky, StringArray &changelists,
+ StatusCallback *callback);
void list(const char *url, Revision &revision, Revision &pegRevision,
svn_depth_t depth, int direntFields, bool fetchLocks,
ListCallback *callback);
diff --git a/subversion/bindings/javahl/native/SVNRepos.cpp b/subversion/bindings/javahl/native/SVNRepos.cpp
index 7fdf63c..0ee87e6 100644
--- a/subversion/bindings/javahl/native/SVNRepos.cpp
+++ b/subversion/bindings/javahl/native/SVNRepos.cpp
@@ -26,7 +26,6 @@
#include "SVNRepos.h"
#include "CreateJ.h"
-#include "ReposNotifyCallback.h"
#include "JNIUtil.h"
#include "svn_error_codes.h"
#include "svn_repos.h"
@@ -50,14 +49,14 @@ SVNRepos *SVNRepos::getCppObject(jobject jthis)
{
static jfieldID fid = 0;
jlong cppAddr = SVNBase::findCppAddrForJObject(jthis, &fid,
- JAVA_PACKAGE"/SVNRepos");
+ JAVAHL_CLASS("/SVNRepos"));
return (cppAddr == 0 ? NULL : reinterpret_cast<SVNRepos *>(cppAddr));
}
void SVNRepos::dispose(jobject jthis)
{
static jfieldID fid = 0;
- SVNBase::dispose(jthis, &fid, JAVA_PACKAGE"/SVNRepos");
+ SVNBase::dispose(jthis, &fid, JAVAHL_CLASS("/SVNRepos"));
}
void SVNRepos::cancelOperation()
@@ -124,8 +123,10 @@ void SVNRepos::deltify(File &path, Revision &revStart, Revision &revEnd)
return;
}
- SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool),
- NULL, requestPool.getPool()), );
+ SVN_JNI_ERR(svn_repos_open3(&repos,
+ path.getInternalStyle(requestPool), NULL,
+ requestPool.getPool(),
+ requestPool.getPool()), );
fs = svn_repos_fs(repos);
SVN_JNI_ERR(svn_fs_youngest_rev(&youngest, fs, requestPool.getPool()), );
@@ -193,8 +194,9 @@ void SVNRepos::dump(File &path, OutputStream &dataOut,
return;
}
- SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool),
- NULL, requestPool.getPool()), );
+ SVN_JNI_ERR(svn_repos_open3(&repos,
+ path.getInternalStyle(requestPool), NULL,
+ requestPool.getPool(), requestPool.getPool()), );
fs = svn_repos_fs(repos);
SVN_JNI_ERR(svn_fs_youngest_rev(&youngest, fs, requestPool.getPool()), );
@@ -248,7 +250,8 @@ void SVNRepos::dump(File &path, OutputStream &dataOut,
}
void SVNRepos::hotcopy(File &path, File &targetPath,
- bool cleanLogs, bool incremental)
+ bool cleanLogs, bool incremental,
+ ReposNotifyCallback *notifyCallback)
{
SVN::Pool requestPool;
@@ -264,9 +267,13 @@ void SVNRepos::hotcopy(File &path, File &targetPath,
return;
}
- SVN_JNI_ERR(svn_repos_hotcopy2(path.getInternalStyle(requestPool),
+ SVN_JNI_ERR(svn_repos_hotcopy3(path.getInternalStyle(requestPool),
targetPath.getInternalStyle(requestPool),
cleanLogs, incremental,
+ notifyCallback != NULL
+ ? ReposNotifyCallback::notify
+ : NULL,
+ notifyCallback,
checkCancel, this /* cancel callback/baton */,
requestPool.getPool()),
);
@@ -321,6 +328,8 @@ void SVNRepos::load(File &path,
bool forceUUID,
bool usePreCommitHook,
bool usePostCommitHook,
+ bool validateProps,
+ bool ignoreDates,
const char *relativePath,
ReposNotifyCallback *notifyCallback)
{
@@ -353,13 +362,14 @@ void SVNRepos::load(File &path,
_("First revision cannot be higher than second")), );
}
- SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool),
- NULL, requestPool.getPool()), );
+ SVN_JNI_ERR(svn_repos_open3(&repos,
+ path.getInternalStyle(requestPool), NULL,
+ requestPool.getPool(), requestPool.getPool()), );
- SVN_JNI_ERR(svn_repos_load_fs4(repos, dataIn.getStream(requestPool),
+ SVN_JNI_ERR(svn_repos_load_fs5(repos, dataIn.getStream(requestPool),
lower, upper, uuid_action, relativePath,
usePreCommitHook, usePostCommitHook,
- FALSE,
+ validateProps, ignoreDates,
notifyCallback != NULL
? ReposNotifyCallback::notify
: NULL,
@@ -380,8 +390,9 @@ void SVNRepos::lstxns(File &path, MessageReceiver &messageReceiver)
return;
}
- SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool),
- NULL, requestPool.getPool()), );
+ SVN_JNI_ERR(svn_repos_open3(&repos,
+ path.getInternalStyle(requestPool), NULL,
+ requestPool.getPool(), requestPool.getPool()), );
fs = svn_repos_fs(repos);
SVN_JNI_ERR(svn_fs_list_transactions(&txns, fs, requestPool.getPool()), );
@@ -417,8 +428,9 @@ jlong SVNRepos::recover(File &path, ReposNotifyCallback *notifyCallback)
/* Since db transactions may have been replayed, it's nice to tell
* people what the latest revision is. It also proves that the
* recovery actually worked. */
- SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool),
- NULL, requestPool.getPool()), -1);
+ SVN_JNI_ERR(svn_repos_open3(&repos,
+ path.getInternalStyle(requestPool), NULL,
+ requestPool.getPool(), requestPool.getPool()), -1);
SVN_JNI_ERR(svn_fs_youngest_rev(&youngest_rev, svn_repos_fs(repos),
requestPool.getPool()),
-1);
@@ -463,8 +475,9 @@ void SVNRepos::rmtxns(File &path, StringArray &transactions)
return;
}
- SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool),
- NULL, requestPool.getPool()), );
+ SVN_JNI_ERR(svn_repos_open3(&repos,
+ path.getInternalStyle(requestPool), NULL,
+ requestPool.getPool(), requestPool.getPool()), );
fs = svn_repos_fs(repos);
args = transactions.array(requestPool);
@@ -520,8 +533,9 @@ void SVNRepos::setRevProp(File &path, Revision &revision,
/* Open the filesystem. */
svn_repos_t *repos;
- SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool),
- NULL, requestPool.getPool()), );
+ SVN_JNI_ERR(svn_repos_open3(&repos,
+ path.getInternalStyle(requestPool), NULL,
+ requestPool.getPool(), requestPool.getPool()), );
/* If we are bypassing the hooks system, we just hit the filesystem
* directly. */
@@ -579,7 +593,9 @@ SVNRepos::getRevnum(svn_revnum_t *revnum, const svn_opt_revision_t *revision,
void
SVNRepos::verify(File &path, Revision &revisionStart, Revision &revisionEnd,
- ReposNotifyCallback *notifyCallback)
+ bool checkNormalization, bool metadataOnly,
+ ReposNotifyCallback *notifyCallback,
+ ReposVerifyCallback *verifyCallback)
{
SVN::Pool requestPool;
svn_repos_t *repos;
@@ -594,8 +610,9 @@ SVNRepos::verify(File &path, Revision &revisionStart, Revision &revisionEnd,
/* This whole process is basically just a dump of the repository
* with no interest in the output. */
- SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool),
- NULL, requestPool.getPool()), );
+ SVN_JNI_ERR(svn_repos_open3(&repos,
+ path.getInternalStyle(requestPool), NULL,
+ requestPool.getPool(), requestPool.getPool()), );
SVN_JNI_ERR(svn_fs_youngest_rev(&youngest, svn_repos_fs(repos),
requestPool.getPool()), );
@@ -621,11 +638,15 @@ SVNRepos::verify(File &path, Revision &revisionStart, Revision &revisionEnd,
(SVN_ERR_INCORRECT_PARAMS, NULL,
_("Start revision cannot be higher than end revision")), );
- SVN_JNI_ERR(svn_repos_verify_fs2(repos, lower, upper,
- notifyCallback != NULL
- ? ReposNotifyCallback::notify
- : NULL,
+ SVN_JNI_ERR(svn_repos_verify_fs3(repos, lower, upper,
+ checkNormalization,
+ metadataOnly,
+ (!notifyCallback ? NULL
+ : ReposNotifyCallback::notify),
notifyCallback,
+ (!verifyCallback ? NULL
+ : ReposVerifyCallback::callback),
+ verifyCallback,
checkCancel, this /* cancel callback/baton */,
requestPool.getPool()), );
}
@@ -641,8 +662,9 @@ void SVNRepos::pack(File &path, ReposNotifyCallback *notifyCallback)
return;
}
- SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool),
- NULL, requestPool.getPool()), );
+ SVN_JNI_ERR(svn_repos_open3(&repos,
+ path.getInternalStyle(requestPool), NULL,
+ requestPool.getPool(), requestPool.getPool()), );
SVN_JNI_ERR(svn_repos_fs_pack2(repos,
notifyCallback != NULL
@@ -686,15 +708,16 @@ jobject SVNRepos::lslocks(File &path, svn_depth_t depth)
return NULL;
}
- SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool),
- NULL, requestPool.getPool()), NULL);
+ SVN_JNI_ERR(svn_repos_open3(&repos,
+ path.getInternalStyle(requestPool), NULL,
+ requestPool.getPool(), requestPool.getPool()), NULL);
/* Fetch all locks on or below the root directory. */
SVN_JNI_ERR(svn_repos_fs_get_locks2(&locks, repos, "/", depth, NULL, NULL,
requestPool.getPool()),
NULL);
JNIEnv *env = JNIUtil::getEnv();
- jclass clazz = env->FindClass(JAVA_PACKAGE"/types/Lock");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/types/Lock"));
if (JNIUtil::isJavaExceptionThrown())
return NULL;
@@ -730,8 +753,9 @@ void SVNRepos::rmlocks(File &path, StringArray &locks)
return;
}
- SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool),
- NULL, requestPool.getPool()), );
+ SVN_JNI_ERR(svn_repos_open3(&repos,
+ path.getInternalStyle(requestPool), NULL,
+ requestPool.getPool(), requestPool.getPool()), );
fs = svn_repos_fs(repos);
const char *username = NULL;
diff --git a/subversion/bindings/javahl/native/SVNRepos.h b/subversion/bindings/javahl/native/SVNRepos.h
index b22a4c4..4fdc48d 100644
--- a/subversion/bindings/javahl/native/SVNRepos.h
+++ b/subversion/bindings/javahl/native/SVNRepos.h
@@ -35,6 +35,7 @@
#include "InputStream.h"
#include "MessageReceiver.h"
#include "ReposNotifyCallback.h"
+#include "ReposVerifyCallback.h"
#include "ReposFreezeAction.h"
#include "StringArray.h"
#include "File.h"
@@ -45,7 +46,9 @@ class SVNRepos : public SVNBase
void rmlocks(File &path, StringArray &locks);
jobject lslocks(File &path, svn_depth_t depth);
void verify(File &path, Revision &revisionStart, Revision &revisionEnd,
- ReposNotifyCallback *notifyCallback);
+ bool checkNormalization, bool metadataOnly,
+ ReposNotifyCallback *notifyCallback,
+ ReposVerifyCallback *verifyCallback);
void setRevProp(File &path, Revision &revision,
const char *propName, const char *propValue,
bool usePreRevPropChangeHook,
@@ -58,11 +61,13 @@ class SVNRepos : public SVNBase
Revision &revsionStart, Revision &revisionEnd,
bool ignoreUUID, bool forceUUID,
bool usePreCommitHook, bool usePostCommitHook,
+ bool validateProps, bool ignoreDates,
const char *relativePath, ReposNotifyCallback *notifyCallback);
void listUnusedDBLogs(File &path,
MessageReceiver &messageReceiver);
void listDBLogs(File &path, MessageReceiver &messageReceiver);
- void hotcopy(File &path, File &targetPath, bool cleanLogs, bool incremental);
+ void hotcopy(File &path, File &targetPath, bool cleanLogs, bool incremental,
+ ReposNotifyCallback *notifyCallback);
void dump(File &path, OutputStream &dataOut, Revision &revsionStart,
Revision &RevisionEnd, bool incremental, bool useDeltas,
ReposNotifyCallback *notifyCallback);
diff --git a/subversion/bindings/javahl/native/StateReporter.cpp b/subversion/bindings/javahl/native/StateReporter.cpp
new file mode 100644
index 0000000..a0c05ed
--- /dev/null
+++ b/subversion/bindings/javahl/native/StateReporter.cpp
@@ -0,0 +1,191 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file StateReporter.cpp
+ * @brief Implementation of the class StateReporter
+ */
+
+#include <jni.h>
+
+#include "JNIUtil.h"
+#include "JNIStringHolder.h"
+#include "StateReporter.h"
+#include "EnumMapper.h"
+#include "Path.h"
+
+#include "svn_private_config.h"
+
+StateReporter::StateReporter()
+ : m_valid(false),
+ m_raw_reporter(NULL),
+ m_report_baton(NULL),
+ m_editor(NULL),
+ m_target_revision(SVN_INVALID_REVNUM)
+{}
+
+StateReporter::~StateReporter()
+{
+ delete m_editor;
+}
+
+StateReporter*
+StateReporter::getCppObject(jobject jthis)
+{
+ static jfieldID fid = 0;
+ jlong cppAddr = SVNBase::findCppAddrForJObject(jthis, &fid,
+ JAVAHL_CLASS("/remote/StateReporter"));
+ return (cppAddr == 0 ? NULL : reinterpret_cast<StateReporter*>(cppAddr));
+}
+
+void
+StateReporter::dispose(jobject jthis)
+{
+ //DEBUG:fprintf(stderr, " (n) StateReporter::dispose()\n");
+
+ if (m_valid)
+ abortReport();
+
+ static jfieldID fid = 0;
+ SVNBase::dispose(jthis, &fid, JAVAHL_CLASS("/remote/StateReporter"));
+}
+
+namespace {
+void throw_reporter_inactive()
+{
+ JNIUtil::raiseThrowable("java/lang/IllegalStateException",
+ _("The reporter is not active"));
+}
+} // anonymous namespace
+
+void
+StateReporter::setPath(jstring jpath, jlong jrevision, jobject jdepth,
+ jboolean jstart_empty, jstring jlock_token)
+{
+ //DEBUG:fprintf(stderr, " (n) StateReporter::setPath()\n");
+
+ if (!m_valid) { throw_reporter_inactive(); return; }
+
+ JNIStringHolder lock_token(jlock_token);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ SVN::Pool subPool(pool);
+ Relpath path(jpath, subPool);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ svn_depth_t depth = EnumMapper::toDepth(jdepth);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ SVN_JNI_ERR(m_raw_reporter->set_path(m_report_baton, path.c_str(),
+ svn_revnum_t(jrevision), depth,
+ bool(jstart_empty), lock_token.c_str(),
+ subPool.getPool()),);
+}
+
+void
+StateReporter::deletePath(jstring jpath)
+{
+ //DEBUG:fprintf(stderr, " (n) StateReporter::deletePath()\n");
+
+ if (!m_valid) { throw_reporter_inactive(); return; }
+
+ SVN::Pool subPool(pool);
+ Relpath path(jpath, subPool);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ SVN_JNI_ERR(m_raw_reporter->delete_path(m_report_baton, path.c_str(),
+ subPool.getPool()),);
+}
+
+void
+StateReporter::linkPath(jstring jurl, jstring jpath,
+ jlong jrevision, jobject jdepth,
+ jboolean jstart_empty, jstring jlock_token)
+{
+ //DEBUG:fprintf(stderr, " (n) StateReporter::linkPath()\n");
+
+ if (!m_valid) { throw_reporter_inactive(); return; }
+
+ JNIStringHolder lock_token(jlock_token);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ SVN::Pool subPool(pool);
+ Relpath path(jpath, subPool);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ URL url(jurl, subPool);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ svn_depth_t depth = EnumMapper::toDepth(jdepth);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ SVN_JNI_ERR(m_raw_reporter->link_path(m_report_baton, path.c_str(),
+ url.c_str(), svn_revnum_t(jrevision),
+ depth, bool(jstart_empty),
+ lock_token.c_str(),
+ subPool.getPool()),);
+}
+
+jlong
+StateReporter::finishReport()
+{
+ //DEBUG:fprintf(stderr, " (n) StateReporter::finishReport()\n");
+
+ if (!m_valid) { throw_reporter_inactive(); return SVN_INVALID_REVNUM; }
+
+ SVN::Pool subPool(pool);
+ SVN_JNI_ERR(m_raw_reporter->finish_report(m_report_baton,
+ subPool.getPool()),
+ SVN_INVALID_REVNUM);
+ m_valid = false;
+ return jlong(m_target_revision);
+}
+
+void
+StateReporter::abortReport()
+{
+ //DEBUG:fprintf(stderr, " (n) StateReporter::abortReport()\n");
+
+ if (!m_valid) { throw_reporter_inactive(); return; }
+
+ SVN::Pool subPool(pool);
+ SVN_JNI_ERR(m_raw_reporter->abort_report(m_report_baton,
+ subPool.getPool()),);
+ m_valid = false;
+}
+
+void
+StateReporter::set_reporter_data(const svn_ra_reporter3_t* raw_reporter,
+ void* report_baton,
+ EditorProxy* editor)
+{
+ //DEBUG:fprintf(stderr, " (n) StateReporter::set_reporter_data()\n");
+
+ m_editor = editor;
+ m_raw_reporter = raw_reporter;
+ m_report_baton = report_baton;
+ m_valid = true;
+}
diff --git a/subversion/bindings/javahl/native/StateReporter.h b/subversion/bindings/javahl/native/StateReporter.h
new file mode 100644
index 0000000..f16ddbf
--- /dev/null
+++ b/subversion/bindings/javahl/native/StateReporter.h
@@ -0,0 +1,74 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file UpdateReporter.h
+ * @brief Interface of the class UpdateReporter
+ */
+
+#ifndef JAVAHL_UPDATE_REPORTER_H
+#define JAVAHL_UPDATE_REPORTER_H
+
+#include <jni.h>
+
+#include "svn_ra.h"
+#include "SVNBase.h"
+#include "EditorProxy.h"
+
+class RemoteSession;
+
+/*
+ * This class wraps the update/status/switch/diff reporter in svn_ra.h
+ */
+class StateReporter : public SVNBase
+{
+public:
+ StateReporter();
+ virtual ~StateReporter();
+
+ static StateReporter* getCppObject(jobject jreporter);
+
+ virtual void dispose(jobject jthis);
+
+ void setPath(jstring jpath, jlong jrevision, jobject jdepth,
+ jboolean jstart_empty, jstring jlock_token);
+ void deletePath(jstring jpath);
+ void linkPath(jstring jurl, jstring jpath, jlong jrevision, jobject jdepth,
+ jboolean jstart_empty, jstring jlock_token);
+ jlong finishReport();
+ void abortReport();
+
+private:
+
+ bool m_valid;
+ const svn_ra_reporter3_t* m_raw_reporter;
+ void* m_report_baton;
+ EditorProxy* m_editor;
+
+ friend class RemoteSession;
+ apr_pool_t* get_report_pool() const { return pool.getPool(); }
+ void set_reporter_data(const svn_ra_reporter3_t* raw_reporter,
+ void* report_baton,
+ EditorProxy* editor);
+ svn_revnum_t m_target_revision;
+};
+
+#endif // JAVAHL_UPDATE_REPORTER_H
diff --git a/subversion/bindings/javahl/native/StatusCallback.cpp b/subversion/bindings/javahl/native/StatusCallback.cpp
index cc0d67a..94ed676 100644
--- a/subversion/bindings/javahl/native/StatusCallback.cpp
+++ b/subversion/bindings/javahl/native/StatusCallback.cpp
@@ -81,13 +81,13 @@ StatusCallback::doStatus(const char *local_abspath,
// it can be cached.
if (mid == 0)
{
- jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/StatusCallback");
+ jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/StatusCallback"));
if (JNIUtil::isJavaExceptionThrown())
POP_AND_RETURN(SVN_NO_ERROR);
mid = env->GetMethodID(clazz, "doStatus",
"(Ljava/lang/String;"
- "L"JAVA_PACKAGE"/types/Status;)V");
+ JAVAHL_ARG("/types/Status;") ")V");
if (JNIUtil::isJavaExceptionThrown() || mid == 0)
POP_AND_RETURN(SVN_NO_ERROR);
}
@@ -101,11 +101,8 @@ StatusCallback::doStatus(const char *local_abspath,
POP_AND_RETURN(SVN_NO_ERROR);
env->CallVoidMethod(m_callback, mid, jPath, jStatus);
- // We return here regardless of whether an exception is thrown or not,
- // so we do not need to explicitly check for one.
- env->PopLocalFrame(NULL);
- return SVN_NO_ERROR;
+ POP_AND_RETURN_EXCEPTION_AS_SVNERROR();
}
void
diff --git a/subversion/bindings/javahl/native/libsvnjavahl.la.c b/subversion/bindings/javahl/native/SubversionException.cpp
index 0cbbd2e..b0b5286 100644
--- a/subversion/bindings/javahl/native/libsvnjavahl.la.c
+++ b/subversion/bindings/javahl/native/SubversionException.cpp
@@ -19,19 +19,14 @@
* under the License.
* ====================================================================
* @endcopyright
- *
- * @file libsvnjavahl.c
- * @brief mandatory file for building with libtool
*/
-#include <jni.h>
-JNIEXPORT jint JNICALL
-JNI_OnLoad(JavaVM *vm, void *reserved)
-{
- return JNI_VERSION_1_2;
-}
+#include "SubversionException.hpp"
+#include "JNIUtil.h"
+
+namespace JavaHL {
+
+const char* const SubversionException::m_class_name =
+ JAVAHL_CLASS("/SubversionException");
-JNIEXPORT void JNICALL
-JNI_OnUnload(JavaVM *vm, void *reserved)
-{
-}
+} // namespace JavaHL
diff --git a/subversion/bindings/javahl/native/SubversionException.hpp b/subversion/bindings/javahl/native/SubversionException.hpp
new file mode 100644
index 0000000..5c71107
--- /dev/null
+++ b/subversion/bindings/javahl/native/SubversionException.hpp
@@ -0,0 +1,54 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#ifndef SVN_JAVAHL_SUBVERSION_EXCEPTION_HPP
+#define SVN_JAVAHL_SUBVERSION_EXCEPTION_HPP
+
+#include "jniwrapper/jni_exception.hpp"
+
+namespace JavaHL {
+
+/**
+ * Generator class for exceptions of type
+ * @c org.apache.subversion.javahl.SubversionException.
+ *
+ * The associated JNI class reference is stored for the lifetime of
+ * the JVM in the global class cache.
+ *
+ * @since New in 1.9.
+ */
+class SubversionException : public ::Java::Exception
+{
+public:
+ explicit SubversionException(::Java::Env env)
+ : Java::Exception(env, ::Java::ClassCache::get_subversion_exception(env))
+ {}
+
+private:
+ friend class ::Java::ClassCacheImpl;
+ static const char* const m_class_name;
+};
+
+} // namespace JavaHL
+
+#endif // SVN_JAVAHL_SUBVERSION_EXCEPTION_HPP
diff --git a/subversion/bindings/javahl/native/Utility.cpp b/subversion/bindings/javahl/native/Utility.cpp
new file mode 100644
index 0000000..9e30386
--- /dev/null
+++ b/subversion/bindings/javahl/native/Utility.cpp
@@ -0,0 +1,96 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#include <string>
+#include <apr_strings.h>
+
+#include "svn_string.h"
+
+#include "jniwrapper/jni_array.hpp"
+#include "jniwrapper/jni_string.hpp"
+#include "jniwrapper/jni_string_map.hpp"
+
+#include "Utility.hpp"
+
+
+namespace JavaHL {
+namespace Util {
+
+namespace {
+class MapToHashIteration
+{
+public:
+ explicit MapToHashIteration(const svn_string_t* default_value,
+ apr_pool_t* pool)
+ : m_pool(pool),
+ m_hash(apr_hash_make(pool)),
+ m_default(default_value)
+ {}
+
+ void operator()(const std::string& key, const Java::ByteArray& value)
+ {
+ const char* const safe_key =
+ apr_pstrmemdup(m_pool, key.c_str(), key.size() + 1);
+ if (!value.get())
+ {
+ if (m_default != NULL)
+ apr_hash_set(m_hash, safe_key, key.size(), m_default);
+ }
+ else
+ {
+ Java::ByteArray::Contents val(value);
+ apr_hash_set(m_hash, safe_key, key.size(), val.get_string(m_pool));
+ }
+ }
+
+ apr_hash_t* get() const
+ {
+ return m_hash;
+ }
+
+private:
+ apr_pool_t* const m_pool;
+ apr_hash_t* const m_hash;
+ const svn_string_t* const m_default;
+};
+
+typedef ::Java::ImmutableMap< ::Java::ByteArray, jbyteArray> ImmutableByteArrayMap;
+} // anonymous namespace
+
+apr_hash_t*
+make_keyword_hash(::Java::Env env, jobject jkeywords, apr_pool_t* pool)
+{
+ const svn_string_t* const empty = svn_string_create_empty(pool);
+ const ImmutableByteArrayMap keywords(env, jkeywords);
+ return keywords.for_each(MapToHashIteration(empty, pool)).get();
+}
+
+apr_hash_t*
+make_property_hash(::Java::Env env, jobject jproperties, apr_pool_t* pool)
+{
+ const ImmutableByteArrayMap props(env, jproperties);
+ return props.for_each(MapToHashIteration(NULL, pool)).get();
+}
+
+} // namespace Util
+} // namespace JavaHL
diff --git a/subversion/bindings/javahl/native/Utility.hpp b/subversion/bindings/javahl/native/Utility.hpp
new file mode 100644
index 0000000..7d91c91
--- /dev/null
+++ b/subversion/bindings/javahl/native/Utility.hpp
@@ -0,0 +1,90 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#ifndef SVN_JAVAHL_UTILITY_HPP
+#define SVN_JAVAHL_UTILITY_HPP
+
+#include <apr_hash.h>
+
+#include "Pool.h"
+
+namespace JavaHL {
+namespace Util {
+
+/**
+ * Converts keyword/valuue pairs in the the Java map @a jkeywords to
+ * an APR hash table allocated in @a pool. The keys in the resulting
+ * table are @c const @c char*, the values are @c svn_string_t*. Null
+ * values in the Java map are converted to empty strings.
+ *
+ * @since New in 1.9.
+ */
+apr_hash_t*
+make_keyword_hash(::Java::Env env, jobject jkeywords, apr_pool_t* pool);
+
+/**
+ * Converts keyword/valuue pairs in the the Java map @a jkeywords to
+ * an APR hash table allocated in @a pool. The keys in the resulting
+ * table are @c const @c char*, the values are @c svn_string_t*. Null
+ * values in the Java map are converted to empty strings.
+ *
+ * @since New in 1.9.
+ */
+inline apr_hash_t*
+make_keyword_hash(::Java::Env env, jobject jkeywords,
+ const ::SVN::Pool& pool)
+{
+ return make_keyword_hash(env, jkeywords, pool.getPool());
+}
+
+
+/**
+ * Converts property/value pairs the Java map @a jproperties to an APR
+ * hash table allocated in @a pool. The keys in the resulting table
+ * are @c const @c char*, the values are @c svn_string_t*. Null values
+ * in the Java map will not appear in the converted map.
+ *
+ * @since New in 1.9.
+ */
+apr_hash_t*
+make_property_hash(::Java::Env env, jobject jproperties, apr_pool_t* pool);
+
+/**
+ * Converts property/value pairs the Java map @a jproperties to an APR
+ * hash table allocated in @a pool. The keys in the resulting table
+ * are @c const @c char*, the values are @c svn_string_t*. Null values
+ * in the Java map will not appear in the converted map.
+ *
+ * @since New in 1.9.
+ */
+inline apr_hash_t*
+make_property_hash(::Java::Env env, jobject jproperties,
+ const ::SVN::Pool& pool)
+{
+ return make_property_hash(env, jproperties, pool.getPool());
+}
+
+} // namespace Util
+} // namespace JavaHL
+
+#endif // SVN_JAVAHL_JNI_UTILITY_HPP
diff --git a/subversion/bindings/javahl/native/VersionExtended.cpp b/subversion/bindings/javahl/native/VersionExtended.cpp
index 1dc47db..7202778 100644
--- a/subversion/bindings/javahl/native/VersionExtended.cpp
+++ b/subversion/bindings/javahl/native/VersionExtended.cpp
@@ -27,7 +27,7 @@
#include "JNIUtil.h"
#include "VersionExtended.h"
-const VersionExtended *
+VersionExtended *
VersionExtended::getCppObject(jobject jthis)
{
if (!jthis)
@@ -35,7 +35,7 @@ VersionExtended::getCppObject(jobject jthis)
static jfieldID fid = 0;
jlong cppAddr = SVNBase::findCppAddrForJObject(
- jthis, &fid, JAVA_PACKAGE"/types/VersionExtended");
+ jthis, &fid, JAVAHL_CLASS("/types/VersionExtended"));
return (cppAddr == 0 ? NULL : reinterpret_cast<VersionExtended *>(cppAddr));
}
@@ -46,7 +46,7 @@ static jobject getWrapperAddress(jobject jthat, volatile jfieldID *fid)
if (!*fid)
{
*fid = env->GetFieldID(env->GetObjectClass(jthat), "wrapper",
- "L"JAVA_PACKAGE"/types/VersionExtended;");
+ JAVAHL_ARG("/types/VersionExtended;"));
if (JNIUtil::isJavaExceptionThrown())
{
*fid = 0;
@@ -94,5 +94,5 @@ VersionExtended::~VersionExtended() {}
void VersionExtended::dispose(jobject jthis)
{
static jfieldID fid = 0;
- SVNBase::dispose(jthis, &fid, JAVA_PACKAGE"/types/VersionExtended");
+ SVNBase::dispose(jthis, &fid, JAVAHL_CLASS("/types/VersionExtended"));
}
diff --git a/subversion/bindings/javahl/native/VersionExtended.h b/subversion/bindings/javahl/native/VersionExtended.h
index 8df81f3..4b5ff48 100644
--- a/subversion/bindings/javahl/native/VersionExtended.h
+++ b/subversion/bindings/javahl/native/VersionExtended.h
@@ -33,7 +33,7 @@
class VersionExtended : public SVNBase
{
public:
- static const VersionExtended *getCppObject(jobject jthis);
+ static VersionExtended *getCppObject(jobject jthis);
static const VersionExtended *getCppObjectFromLinkedLib(jobject jthat);
static const VersionExtended *getCppObjectFromLoadedLib(jobject jthat);
static const VersionExtended *getCppObjectFromLinkedLibIterator(jobject jthat);
diff --git a/subversion/bindings/javahl/native/deprecated.cpp b/subversion/bindings/javahl/native/deprecated.cpp
new file mode 100644
index 0000000..2ba5e10
--- /dev/null
+++ b/subversion/bindings/javahl/native/deprecated.cpp
@@ -0,0 +1,60 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file deprecated.cpp
+ * @brief: Implementation of methods that intentionally use deprecated
+ * Subversion APIs.
+ */
+
+/* We define this here to remove any further warnings about the usage of
+ deprecated functions in this file. */
+#define SVN_DEPRECATED
+
+#include "SVNClient.h"
+#include "JNIUtil.h"
+#include "Path.h"
+#include "Revision.h"
+
+#include "svn_client.h"
+
+void SVNClient::mergeReintegrate(const char *path, Revision &pegRevision,
+ const char *localPath, bool dryRun)
+{
+ SVN::Pool subPool(pool);
+ SVN_JNI_NULL_PTR_EX(path, "path", );
+ SVN_JNI_NULL_PTR_EX(localPath, "localPath", );
+ Path intLocalPath(localPath, subPool);
+ SVN_JNI_ERR(intLocalPath.error_occurred(), );
+
+ Path srcPath(path, subPool);
+ SVN_JNI_ERR(srcPath.error_occurred(), );
+
+ svn_client_ctx_t *ctx = context.getContext(NULL, subPool);
+ if (ctx == NULL)
+ return;
+
+ SVN_JNI_ERR(svn_client_merge_reintegrate(srcPath.c_str(),
+ pegRevision.revision(),
+ intLocalPath.c_str(),
+ dryRun, NULL, ctx,
+ subPool.getPool()), );
+}
diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_array.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_array.hpp
new file mode 100644
index 0000000..0c92e7a
--- /dev/null
+++ b/subversion/bindings/javahl/native/jniwrapper/jni_array.hpp
@@ -0,0 +1,267 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#ifndef SVN_JAVAHL_JNIWRAPPER_ARRAY_HPP
+#define SVN_JAVAHL_JNIWRAPPER_ARRAY_HPP
+
+#include <cstring>
+#include <string>
+
+#include "svn_string.h"
+
+#include "jni_env.hpp"
+#include "../Pool.h"
+
+namespace Java {
+
+/**
+ * Wrapper class for Java byte arrays.
+ *
+ * @since New in 1.9.
+ */
+class ByteArray
+{
+public:
+ /**
+ * Constructs a wrapper around an existing @a array.
+ */
+ explicit ByteArray(Env env, jbyteArray array)
+ : m_env(env),
+ m_length(array ? m_env.GetArrayLength(array) : 0),
+ m_array(array)
+ {}
+
+ /**
+ * Constructs a new, uninitialized array of size @a length.
+ */
+ explicit ByteArray(Env env, jsize length)
+ : m_env(env),
+ m_length(length),
+ m_array(m_env.NewByteArray(m_length))
+ {}
+
+ /**
+ * Constructs a new array and wrapper from @a text.
+ */
+ explicit ByteArray(Env env, const char* text)
+ : m_env(env),
+ m_length(jsize(::std::strlen(text))),
+ m_array(m_env.NewByteArray(m_length))
+ {
+ ByteArray::MutableContents contents(*this);
+ ::memcpy(contents.data(), text, m_length);
+ }
+
+ /**
+ * Constructs a new array and wrapper of size @a length
+ * from @a data.
+ */
+ explicit ByteArray(Env env, const void* data, jsize length)
+ : m_env(env),
+ m_length(length),
+ m_array(m_env.NewByteArray(m_length))
+ {
+ ByteArray::MutableContents contents(*this);
+ ::memcpy(contents.data(), data, m_length);
+ }
+
+ /**
+ * Constructs a new array and wrapper from @a text.
+ */
+ explicit ByteArray(Env env, const std::string& text)
+ : m_env(env),
+ m_length(jsize(text.size())),
+ m_array(m_env.NewByteArray(m_length))
+ {
+ ByteArray::MutableContents contents(*this);
+ ::memcpy(contents.data(), text.c_str(), m_length);
+ }
+
+ /**
+ * Returns the wrapped native array reference.
+ */
+ jbyteArray get() const
+ {
+ return m_array;
+ }
+
+ /**
+ * Returns the size of the wrapped array.
+ */
+ jsize length() const
+ {
+ return m_length;
+ }
+
+ /**
+ * Accessor class for the contents of the byte array.
+ *
+ * Objects of this class should be created within the scope where
+ * the raw data stored in the array must be manipulated. They will
+ * create an immutable mirror of the array contents.
+ */
+ class Contents
+ {
+ public:
+ /**
+ * Constructs an immutable array contents accessor.
+ *
+ * Whilst the returned contents are themselves mutable, the
+ * destructor will discard any changes.
+ */
+ explicit Contents(const ByteArray& array)
+ : m_array(array),
+ m_data(!array.m_array ? NULL
+ : array.m_env.GetByteArrayElements(array.m_array, NULL))
+ {}
+
+ /**
+ * Releases the array contents.
+ */
+ ~Contents()
+ {
+ if (m_data)
+ {
+ const Env& env = m_array.m_env;
+ env.ReleaseByteArrayElements(m_array.m_array, m_data, JNI_ABORT);
+ }
+ }
+
+ /**
+ * Returns the address of the immutable array contents.
+ * @note The data will @b not be NUL-terminated!
+ */
+ const char* data() const
+ {
+ return reinterpret_cast<const char*>(m_data);
+ }
+
+ /**
+ * Returns the size of the array contents.
+ */
+ jsize length() const
+ {
+ return m_array.m_length;
+ }
+
+ /**
+ * Copies the array contents to a NUL-terminated string allocated
+ * from @a result_pool.
+ */
+ svn_string_t* get_string(apr_pool_t* result_pool) const
+ {
+ if (m_data)
+ return svn_string_ncreate(data(), m_array.m_length, result_pool);
+ return NULL;
+ }
+
+ /**
+ * Copies the array contents to a NUL-terminated string allocated
+ * from @a result_pool.
+ */
+ svn_string_t* get_string(const ::SVN::Pool& result_pool) const
+ {
+ return get_string(result_pool.getPool());
+ }
+
+ protected:
+ const ByteArray& m_array;
+ jbyte* m_data;
+ };
+
+ /**
+ * Accessor class for the contents of the byte array.
+ *
+ * Behaves like the #Contents class, but the mirrored contents are
+ * considered mutable and any changes made to them will be committed
+ * to the JVM.
+ */
+ class MutableContents : protected Contents
+ {
+ public:
+ /**
+ * Constructs a mutable array contents accessor.
+ */
+ explicit MutableContents(ByteArray& array)
+ : Contents(array)
+ {}
+
+ /**
+ * Releases the array contents, committing changes to the JVM.
+ */
+ ~MutableContents()
+ {
+ if (m_data)
+ {
+ // Prevent double-release by the Contents desctuctor
+ jbyte* const data = m_data;
+ m_data = NULL;
+ m_array.m_env.ReleaseByteArrayElements(m_array.m_array, data, 0);
+ }
+ }
+ /**
+ * Returns the mutable address of the array contents.
+ * @note The data will @b not be NUL-terminated!
+ */
+ char* data()
+ {
+ return const_cast<char*>(Contents::data());
+ }
+
+ /**
+ * Returns the size of the array contents.
+ */
+ jsize length() const
+ {
+ return Contents::length();
+ }
+
+ /**
+ * Copies the array contents to a NUL-terminated string allocated
+ * from @a result_pool.
+ */
+ svn_string_t* get_string(apr_pool_t* result_pool) const
+ {
+ return Contents::get_string(result_pool);
+ }
+
+ /**
+ * Copies the array contents to a NUL-terminated string allocated
+ * from @a result_pool.
+ */
+ svn_string_t* get_string(const ::SVN::Pool& result_pool) const
+ {
+ return Contents::get_string(result_pool.getPool());
+ }
+ };
+
+private:
+ friend class Contents;
+ const Env m_env;
+ const jsize m_length;
+ const jbyteArray m_array;
+};
+
+} // namespace Java
+
+#endif // SVN_JAVAHL_JNIWRAPPER_ARRAY_HPP
diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_base.cpp b/subversion/bindings/javahl/native/jniwrapper/jni_base.cpp
new file mode 100644
index 0000000..e3671a2
--- /dev/null
+++ b/subversion/bindings/javahl/native/jniwrapper/jni_base.cpp
@@ -0,0 +1,382 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#include <cstring>
+#include <memory>
+#include <apr.h>
+
+#include "jni_env.hpp"
+#include "jni_globalref.hpp"
+#include "jni_exception.hpp"
+#include "jni_object.hpp"
+#include "jni_string.hpp"
+#include "jni_array.hpp"
+#include "jni_stack.hpp"
+
+#include "../JNIUtil.h"
+bool initialize_jni_util(JNIEnv *env);
+
+#include "svn_private_config.h"
+
+
+// Global library initializaiton
+
+/**
+ * Initializer function, called just after the JVM loads the native
+ * library. Stores the global JVM reference and creates the global
+ * class cache.
+ */
+JNIEXPORT jint JNICALL
+JNI_OnLoad(JavaVM* jvm, void*)
+{
+ ::Java::Env::static_init(jvm);
+ const ::Java::Env env;
+
+ const apr_status_t status = apr_initialize();
+ if (!status)
+ ::Java::ClassCache::create();
+ else
+ {
+ char buf[2048];
+ std::strcpy(buf, "Could not initialize APR: ");
+ const std::size_t offset = std::strlen(buf);
+ apr_strerror(status, buf + offset, sizeof(buf) - offset - 1);
+ env.ThrowNew(env.FindClass("java/lang/Error"), buf);
+ }
+
+ // Initialize the old-style JavaHL infrastructure.
+ if (!initialize_jni_util(env.get()) && !env.ExceptionCheck())
+ {
+ env.ThrowNew(env.FindClass("java/lang/LinkageError"),
+ "Native library initialization failed");
+ }
+
+ return JNI_VERSION_1_2;
+}
+
+/**
+ * Cleanup function, called just before the JVM unloads the native
+ * library. Destroys the global class cache.
+ */
+JNIEXPORT void JNICALL
+JNI_OnUnload(JavaVM*, void*)
+{
+ ::Java::ClassCache::destroy();
+ apr_terminate();
+}
+
+
+namespace Java {
+
+// class Java::Env
+
+::JavaVM* Env::m_jvm = NULL;
+void Env::static_init(::JavaVM* jvm)
+{
+ m_jvm = jvm;
+}
+
+const char* Env::error_create_global_reference() throw()
+{
+ return _("Could not create global reference");
+}
+
+const char* Env::error_get_contents_string() throw()
+{
+ return _("Could not get contents of Java String");
+}
+
+const char* Env::error_release_null_string() throw()
+{
+ return _("Could not release contents of a null String");
+}
+
+const char* Env::error_create_object_array() throw()
+{
+ return _("Could not create Object array");
+}
+
+namespace {
+// The typed array error messages are always fatal, so allocating
+// the buffer on the heap and never releasing it does not really
+// constitute a memory leak.
+const char* make_typed_error(const char* fmt, const char* type) throw()
+{
+ const apr_size_t bufsize = 512;
+ char *msg = new(std::nothrow) char[bufsize];
+ apr_snprintf(msg, bufsize, fmt, type);
+ return msg;
+}
+} // anonymous namespace
+
+const char* Env::error_create_array(const char* type) throw()
+{
+ return make_typed_error(_("Could not create %sArray"), type);
+}
+
+const char* Env::error_get_contents_array(const char* type) throw()
+{
+ return make_typed_error(_("Could not get %s array contents"), type);
+}
+
+const char* Env::error_release_null_array(const char* type) throw()
+{
+ return make_typed_error(
+ _("Could not release contents of a null %sArray"), type);
+}
+
+::JNIEnv* Env::env_from_jvm()
+{
+ if (m_jvm)
+ {
+ void *penv;
+ switch (m_jvm->GetEnv(&penv, JNI_VERSION_1_2))
+ {
+ case JNI_OK:
+ return static_cast<JNIEnv*>(penv);
+
+ case JNI_EDETACHED:
+ throw std::runtime_error(
+ _("Native thread is not attached to a Java VM"));
+
+ case JNI_EVERSION:
+ throw std::runtime_error(_("Unsupported JNI version"));
+
+ default:
+ throw std::runtime_error(_("Invalid JNI environment"));
+ }
+ }
+ throw std::logic_error(_("JavaVM instance was not initialized"));
+}
+
+void Env::throw_java_out_of_memory(const char* message) const
+{
+ OutOfMemoryError(*this).raise(message);
+}
+
+// class Java::LocalFrame
+
+const jint LocalFrame::DEFAULT_CAPACITY = 16;
+
+
+// class Java::GlobalObject
+
+GlobalObject& GlobalObject::operator=(jobject that)
+{
+ this->~GlobalObject();
+ return *new(this) GlobalObject(Env(), that);
+}
+
+GlobalObject::~GlobalObject()
+{
+ if (m_obj)
+ Env().DeleteGlobalRef(m_obj);
+}
+
+
+// class Java::GlobalClass
+
+GlobalClass& GlobalClass::operator=(jclass that)
+{
+ this->~GlobalClass();
+ return *new(this) GlobalClass(Env(), that);
+}
+
+
+// Class Java::Object
+
+const char* const Object::m_class_name = "java/lang/Object";
+Object::ClassImpl::~ClassImpl() {}
+
+
+// Class Java::Class
+
+const char* const Class::m_class_name = "java/lang/Class";
+Class::ClassImpl::~ClassImpl() {}
+
+MethodID Class::m_mid_get_class;
+MethodID Class::m_mid_get_name;
+
+void Class::static_init(Env env, jclass cls)
+{
+ m_mid_get_class = env.GetMethodID(
+ ClassCache::get_object(env)->get_class(),
+ "getClass", "()Ljava/lang/Class;");
+ m_mid_get_name = env.GetMethodID(
+ cls, "getName", "()Ljava/lang/String;");
+}
+
+namespace{
+jobject get_class_of_object(Env env, jobject obj, jmethodID mid_get_class)
+{
+ if (!obj)
+ return NULL;
+ return env.CallObjectMethod(obj, mid_get_class);
+}
+} // anonymous namespace
+
+Class::Class(Env env, jobject obj)
+ : m_env(env),
+ m_jthis(get_class_of_object(env, obj, m_mid_get_class))
+{}
+
+Class::Class(const Object& obj)
+ : m_env(obj.get_env()),
+ m_jthis(get_class_of_object(obj.get_env(), obj.get(), m_mid_get_class))
+{}
+
+jstring Class::get_name() const
+{
+ if (!m_jthis)
+ return NULL;
+ return jstring(m_env.CallObjectMethod(m_jthis, m_mid_get_name));
+}
+
+
+// Class Java::String
+
+const char* const String::m_class_name = "java/lang/String";
+String::ClassImpl::~ClassImpl() {}
+
+const char* String::strdup(apr_pool_t* pool) const
+{
+ return apr_pstrdup(pool, String::Contents(*this).c_str());
+}
+
+void String::MutableContents::set_value(const char* new_text)
+{
+ if (!m_new_text)
+ throw std::invalid_argument(
+ _("Cannot set String contents to null"));
+ if (m_text)
+ {
+ m_new_text = new_text;
+ m_length = jsize(::std::strlen(new_text));
+ }
+ else
+ throw std::logic_error(
+ _("Cannot change the contents of a null String"));
+}
+
+// class Java::Exception
+
+void Exception::throw_java_exception() const
+{
+ if (instantiated()
+ ? m_env.Throw(m_jthis)
+ : m_env.ThrowNew(m_class, NULL))
+ throw std::runtime_error(_("Could not throw Java exception"));
+}
+
+void Exception::throw_java_exception(const char* message) const
+{
+ if (m_env.ThrowNew(m_class, message))
+ throw std::runtime_error(_("Could not throw Java exception"));
+}
+
+jstring Exception::get_message() const
+{
+ if (instantiated())
+ return jstring(m_env.CallObjectMethod(m_jthis, m_mid_get_message));
+ throw std::logic_error(_("Could not get exception message:"
+ " Exception instance is not available"));
+}
+
+const char* const Exception::m_class_name = "java/lang/Throwable";
+Exception::ClassImpl::~ClassImpl() {}
+
+MethodID Exception::m_mid_get_message;
+void Exception::static_init(Env env, jclass cls)
+{
+ m_mid_get_message = env.GetMethodID(
+ cls, "getMessage", "()Ljava/lang/String;");
+}
+
+// Other exception class initializers
+
+const char* const RuntimeException::m_class_name =
+ "java/lang/RuntimeException";
+
+const char* const NullPointerException::m_class_name =
+ "java/lang/NullPointerException";
+
+const char* const OutOfMemoryError::m_class_name =
+ "java/lang/OutOfMemoryError";
+
+const char* const IndexOutOfBoundsException::m_class_name =
+ "java/lang/IndexOutOfBoundsException";
+IndexOutOfBoundsException::ClassImpl::~ClassImpl() {}
+
+const char* const IOException::m_class_name =
+ "java/io/IOException";
+
+const char* const IllegalArgumentException::m_class_name =
+ "java/lang/IllegalArgumentException";
+
+const char *const NoSuchElementException::m_class_name =
+ "java/util/NoSuchElementException";
+NoSuchElementException::ClassImpl::~ClassImpl() {}
+
+// Implementation of jni_stack.hpp
+
+void handle_svn_error(Env env, ::svn_error_t* err)
+{
+ jthrowable cause = NULL;
+
+ // 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(env)->get_class()))
+ {
+ // 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();
+ }
+ }
+
+ // Make sure there's only a single exception in the environment.
+ if (cause)
+ env.ExceptionClear();
+
+ ::JNIUtil::handleSVNError(err, cause);
+ throw SignalExceptionThrown();
+}
+
+const char* unknown_cxx_exception_message() throw()
+{
+ return _("Caught unknown C++ exception");
+}
+
+svn_error_t* caught_java_exception_error(apr_status_t status) throw()
+{
+ return svn_error_create(status, JNIUtil::wrapJavaException(),
+ _("Java exception"));
+}
+
+} // namespace Java
diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_channel.cpp b/subversion/bindings/javahl/native/jniwrapper/jni_channel.cpp
new file mode 100644
index 0000000..a3d41b2
--- /dev/null
+++ b/subversion/bindings/javahl/native/jniwrapper/jni_channel.cpp
@@ -0,0 +1,257 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#include <stdexcept>
+
+#include "jni_object.hpp"
+#include "jni_array.hpp"
+#include "jni_channel.hpp"
+
+#include "svn_private_config.h"
+
+namespace Java {
+
+namespace {
+// Get the ByteBuffer's internal array.
+jbyteArray get_array(Env env, jobject buffer,
+ const MethodID& mid_has_array,
+ const MethodID& mid_get_array)
+{
+ if (!env.CallBooleanMethod(buffer, mid_has_array))
+ return NULL;
+ return jbyteArray(env.CallObjectMethod(buffer, mid_get_array));
+}
+
+// Get the offset in the ByteBuffer's array. NEVER call this function
+// unless the buffer actually has an accessible array.
+jint get_array_offset(Env env, jobject buffer,
+ const MethodID& mid_get_array_offset)
+{
+ return env.CallIntMethod(buffer, mid_get_array_offset);
+}
+
+// Get the remaining space in a ByteBuffer.
+jint get_remaining(Env env, jobject buffer,
+ const MethodID& mid_get_remaining)
+{
+ return env.CallIntMethod(buffer, mid_get_remaining);
+}
+
+// Get the current position of a ByteBuffer.
+jint get_position(Env env, jobject buffer,
+ const MethodID& mid_get_position)
+{
+ return env.CallIntMethod(buffer, mid_get_position);
+}
+
+// Set the new position of a ByteBuffer.
+void set_position(Env env, jobject buffer,
+ const MethodID& mid_set_position,
+ jint new_position)
+{
+ env.CallObjectMethod(buffer, mid_set_position, new_position);
+}
+
+// Get byte array contents from a ByteBuffer.
+void get_bytearray(Env env, jobject buffer,
+ const MethodID& mid_get_bytearray,
+ ByteArray& array, jint length = -1, jint offset = 0)
+{
+ env.CallObjectMethod(
+ buffer, mid_get_bytearray, array.get(), offset,
+ (length >= 0 ? length : (array.length() - offset)));
+}
+
+// Put byte array contents into a ByteBuffer.
+void put_bytearray(Env env, jobject buffer,
+ const MethodID& mid_put_bytearray,
+ ByteArray& array, jint length = -1, jint offset = 0)
+{
+ env.CallObjectMethod(buffer, mid_put_bytearray,
+ array.get(), offset,
+ (length >= 0 ? length : (array.length() - offset)));
+}
+
+struct BadReaderWriter : public ChannelReader, ChannelWriter
+{
+ BadReaderWriter() {}
+
+ virtual jint operator()(Env, void*, jint)
+ {
+ throw std::logic_error(_("Reading from write-only channel"));
+ }
+
+ virtual jint operator()(Env, const void*, jint)
+ {
+ throw std::logic_error(_("Writing to read-only channel"));
+ }
+} bad_reader_writer;
+
+} // anonymous namespace
+
+
+ChannelReader& ByteChannel::m_null_reader = bad_reader_writer;
+ChannelWriter& ByteChannel::m_null_writer = bad_reader_writer;
+
+const char* const ByteChannel::ByteBuffer::m_class_name =
+ "java/nio/ByteBuffer";
+
+ByteChannel::ByteBuffer::ClassImpl::ClassImpl(Env env, jclass cls)
+ : Object::ClassImpl(env, cls),
+ m_mid_has_array(env.GetMethodID(cls, "hasArray", "()Z")),
+ m_mid_get_array(env.GetMethodID(cls, "array", "()[B")),
+ m_mid_get_array_offset(env.GetMethodID(cls, "arrayOffset", "()I")),
+ m_mid_get_remaining(env.GetMethodID(cls, "remaining", "()I")),
+ m_mid_get_position(env.GetMethodID(cls, "position", "()I")),
+ m_mid_set_position(env.GetMethodID(cls, "position",
+ "(I)Ljava/nio/Buffer;")),
+ m_mid_get_bytearray(env.GetMethodID(cls, "get",
+ "([BII)Ljava/nio/ByteBuffer;")),
+ m_mid_put_bytearray(env.GetMethodID(cls, "put",
+ "([BII)Ljava/nio/ByteBuffer;"))
+{}
+
+ByteChannel::ByteBuffer::ClassImpl::~ClassImpl() {}
+
+jint ByteChannel::read(jobject destination)
+{
+ const ByteBuffer::ClassImpl& bufimpl = ByteBuffer::impl(m_env);
+
+ const jint remaining = get_remaining(m_env, destination,
+ bufimpl.m_mid_get_remaining);
+ if (!remaining)
+ {
+ // No space in the buffer; don't try to read anything.
+ return 0;
+ }
+
+ const jint position = get_position(m_env, destination,
+ bufimpl.m_mid_get_position);
+
+ jint bytes_read = 0;
+ void* data = m_env.GetDirectBufferAddress(destination);
+ if (data)
+ {
+ data = static_cast<char*>(data) + position;
+ bytes_read = m_reader(m_env, data, remaining);
+ }
+ else
+ {
+ // It was not a direct buffer ... see if it has an array.
+ jbyteArray raw_array = get_array(m_env, destination,
+ bufimpl.m_mid_has_array,
+ bufimpl.m_mid_get_array);
+ if (raw_array)
+ {
+ const jint array_offset = get_array_offset(
+ m_env, destination,
+ bufimpl.m_mid_get_array_offset);
+ ByteArray array(m_env, raw_array);
+ ByteArray::MutableContents contents(array);
+ data = contents.data();
+ data = static_cast<char*>(data) + position + array_offset;
+ bytes_read = m_reader(m_env, data, remaining);
+ }
+ }
+ if (data)
+ {
+ if (bytes_read > 0)
+ set_position(m_env, destination,
+ bufimpl.m_mid_set_position,
+ position + bytes_read);
+ return bytes_read;
+ }
+
+ // No accessible array, either. Oh well. Create a byte array and
+ // push it into the buffer.
+ ByteArray array(m_env, remaining);
+ ByteArray::MutableContents contents(array);
+ bytes_read = m_reader(m_env, contents.data(), contents.length());
+ if (bytes_read > 0)
+ put_bytearray(m_env, destination,
+ bufimpl.m_mid_put_bytearray,
+ array, bytes_read);
+ return bytes_read;
+}
+
+jint ByteChannel::write(jobject source)
+{
+ const ByteBuffer::ClassImpl& bufimpl = ByteBuffer::impl(m_env);
+
+ const jint remaining = get_remaining(m_env, source,
+ bufimpl.m_mid_get_remaining);
+ if (!remaining)
+ {
+ // No data in the buffer; don't try to write anything.
+ return 0;
+ }
+
+ const jint position = get_position(m_env, source,
+ bufimpl.m_mid_get_position);
+
+ jint bytes_written = 0;
+ const void* data = m_env.GetDirectBufferAddress(source);
+ if (data)
+ {
+ data = static_cast<const char*>(data) + position;
+ bytes_written = m_writer(m_env, data, remaining);
+ }
+ else
+ {
+ // It was not a direct buffer ... see if it has an array.
+ jbyteArray raw_array = get_array(m_env, source,
+ bufimpl.m_mid_has_array,
+ bufimpl.m_mid_get_array);
+ if (raw_array)
+ {
+ const jint array_offset = get_array_offset(
+ m_env, source,
+ bufimpl.m_mid_get_array_offset);
+ const ByteArray array(m_env, raw_array);
+ ByteArray::Contents contents(array);
+ data = contents.data();
+ data = static_cast<const char*>(data) + position + array_offset;
+ bytes_written = m_writer(m_env, data, remaining);
+ }
+ }
+ if (data)
+ {
+ if (bytes_written > 0)
+ set_position(m_env, source,
+ bufimpl.m_mid_set_position,
+ position + bytes_written);
+ return bytes_written;
+ }
+
+ // No accessible array, either. Oh well. Get an array from the
+ // buffer and read data from that.
+ ByteArray array(m_env, remaining);
+ get_bytearray(m_env, source,
+ bufimpl.m_mid_get_bytearray,
+ array);
+ ByteArray::Contents contents(array);
+ bytes_written = m_writer(m_env, contents.data(), contents.length());
+ return bytes_written;
+}
+
+} // namespace Java
diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_channel.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_channel.hpp
new file mode 100644
index 0000000..7af5783
--- /dev/null
+++ b/subversion/bindings/javahl/native/jniwrapper/jni_channel.hpp
@@ -0,0 +1,223 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#ifndef SVN_JAVAHL_JNIWRAPPER_CHANNEL_HPP
+#define SVN_JAVAHL_JNIWRAPPER_CHANNEL_HPP
+
+#include "jni_env.hpp"
+
+namespace Java {
+
+/**
+ * Abstract base class for implementing channel read method internals.
+ *
+ * @since New in 1.9.
+ */
+struct ChannelReader
+{
+ /**
+ * Reads at most @a length bytes into @a buffer, returning the
+ * number of bytes read (which may be zero) or -1 if at
+ * end-of-stream.
+ */
+ virtual jint operator()(Env env, void* buffer, jint length) = 0;
+};
+
+/**
+ * Abstract base class for implementing channel write method internals.
+ *
+ * @since New in 1.9.
+ */
+struct ChannelWriter
+{
+ /**
+ * Writes at exactly @a length bytes from @a buffer, returning the
+ * number of bytes written (which may be zero).
+ */
+ virtual jint operator()(Env env, const void* buffer, jint length) = 0;
+};
+
+
+/**
+ * Wrapper for @c java.nio.channels.ByteChannel. Unlike most wrappers,
+ * this one does not actually represent a ByteChannel object. The
+ * assumption is that the native implementation will want to implement
+ * the read and write methods, not invoke them.
+ *
+ * Also serves as the (protected) base of the Readable- and
+ * WritableByteChannel interfaces; this is for purposes of code
+ * sharing only. We're not interested in replicating Java's class
+ * hierarchy here.
+ *
+ * @since New in 1.9.
+ */
+class ByteChannel
+{
+public:
+ /**
+ * Constructs a wrapper for @a channel with @a reader and @a writer
+ * as the read and write method implementations.
+ */
+ explicit ByteChannel(Env env, ChannelReader& reader, ChannelWriter& writer)
+ : m_env(env),
+ m_reader(reader),
+ m_writer(writer)
+ {}
+
+ /**
+ * Reads bytes into @a destination, which must be a
+ * @c java.nio.ByteBuffer instance, from #m_reader.
+ * @return the number of bytes read, or -1 if at end-of-stream.
+ */
+ jint read(jobject destination);
+
+ /**
+ * Writes bytes from @a source, which must be a
+ * @c java.nio.ByteBuffer instance, to #m_writer.
+ * @return the number of bytes written.
+ */
+ jint write(jobject source);
+
+protected:
+ /**
+ * Constructor used by read-only subclasses.
+ */
+ explicit ByteChannel(Env env, ChannelReader& reader)
+ : m_env(env),
+ m_reader(reader),
+ m_writer(m_null_writer)
+ {}
+
+ /**
+ * Constructor used by write-only subclasses.
+ */
+ explicit ByteChannel(Env env, ChannelWriter& writer)
+ : m_env(env),
+ m_reader(m_null_reader),
+ m_writer(writer)
+ {}
+
+private:
+ Env m_env;
+ ChannelReader& m_reader;
+ ChannelWriter& m_writer;
+
+ static ChannelReader& m_null_reader;
+ static ChannelWriter& m_null_writer;
+
+ friend class ClassCacheImpl;
+
+ // Private references for the java.nio.ByteBuffer class.
+ struct ByteBuffer
+ {
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const MethodID m_mid_has_array;
+ const MethodID m_mid_get_array;
+ const MethodID m_mid_get_array_offset;
+ const MethodID m_mid_get_remaining;
+ const MethodID m_mid_get_position;
+ const MethodID m_mid_set_position;
+ const MethodID m_mid_get_bytearray;
+ const MethodID m_mid_put_bytearray;
+ };
+
+ static const char* const m_class_name;
+ static const ClassImpl& impl(Env env)
+ {
+ return *dynamic_cast<const ClassImpl*>(
+ ClassCache::get_byte_buffer(env));
+ }
+ };
+};
+
+
+/**
+ * Wrapper for @c java.nio.channels.ReadableByteChannel.
+ *
+ * @since New in 1.9.
+ */
+class ReadableByteChannel : protected ByteChannel
+{
+public:
+ /**
+ * Constructs a wrapper for @a channel with @a reader the read
+ * method implementation.
+ */
+ explicit ReadableByteChannel(Env env, ChannelReader& reader)
+ : ByteChannel(env, reader)
+ {}
+
+ /**
+ * Reads bytes into @a destination, which must be a
+ * @c java.nio.ByteBuffer instance, from #m_reader.
+ * @return the number of bytes read, or -1 if at end-of-stream.
+ */
+ jint read(jobject destination)
+ {
+ return ByteChannel::read(destination);
+ }
+};
+
+
+/**
+ * Wrapper @c java.nio.channels.WritableByteChannel.
+ *
+ * @since New in 1.9.
+ */
+class WritableByteChannel : protected ByteChannel
+{
+public:
+ /**
+ * Constructs a wrapper for @a channel with @a writer as the write
+ * method implementation.
+ */
+ explicit WritableByteChannel(Env env, ChannelWriter& writer)
+ : ByteChannel(env, writer)
+ {}
+
+ /**
+ * Writes bytes from @a source, which must be a
+ * @c java.nio.ByteBuffer instance, to #m_writer.
+ * @return the number of bytes written.
+ */
+ jint write(jobject source)
+ {
+ return ByteChannel::write(source);
+ }
+};
+
+} // namespace Java
+
+#endif // SVN_JAVAHL_JNIWRAPPER_CHANNEL_HPP
diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_class_cache.cpp b/subversion/bindings/javahl/native/jniwrapper/jni_class_cache.cpp
new file mode 100644
index 0000000..9e533db
--- /dev/null
+++ b/subversion/bindings/javahl/native/jniwrapper/jni_class_cache.cpp
@@ -0,0 +1,326 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#include <stdexcept>
+
+#include <apr_atomic.h>
+
+#define SVN_JAVAHL_JNIWRAPPER_LOG(expr)
+#include "jni_env.hpp"
+#include "jni_globalref.hpp"
+#include "jni_exception.hpp"
+#include "jni_object.hpp"
+#include "jni_string.hpp"
+
+#include "jni_channel.hpp"
+#include "jni_io_stream.hpp"
+#include "jni_list.hpp"
+#include "jni_string_map.hpp"
+
+#include "../SubversionException.hpp"
+#include "../AuthnCallback.hpp"
+#include "../Credential.hpp"
+#include "../ExternalItem.hpp"
+#include "../EditorCallbacks.hpp"
+
+namespace
+{
+/* This class behaves like a dumbed-down std:auto_ptr, but it
+ implements atomic access and modification of the wrapped
+ pointer. */
+class ClassImplPtr
+{
+ typedef ::Java::Object::ClassImpl ClassImpl;
+
+ public:
+ /* Default constructor; initializes the wrapped pointer to NULL */
+ explicit ClassImplPtr()
+ : m_ptr(NULL)
+ {}
+
+ /* Initializing constructor; sets the wrapped pointer to PTR */
+ explicit ClassImplPtr(ClassImpl* ptr)
+ : m_ptr(ptr)
+ {}
+
+ /* Destructor deletes the object and resets the wrapped pointer to NULL. */
+ ~ClassImplPtr()
+ {
+ delete static_cast<ClassImpl*>(
+ apr_atomic_casptr(&m_ptr, NULL, get()));
+ }
+
+ /* Sets the wrapped pointer to PTR iff it is NULL, and returns the
+ old value. */
+ ClassImpl* test_and_set(ClassImpl* ptr)
+ {
+ return static_cast<ClassImpl*>(
+ apr_atomic_casptr(&m_ptr, ptr, NULL));
+ }
+
+ /* Returns the current value of the the wrapped pointer. */
+ ClassImpl* get() const
+ {
+ return static_cast<ClassImpl*>(
+ apr_atomic_casptr(&m_ptr, NULL, NULL));
+ }
+
+private:
+ // Non-copyable
+ ClassImplPtr(const ClassImplPtr&);
+ ClassImplPtr& operator=(const ClassImplPtr&);
+
+ mutable volatile void* m_ptr;
+};
+} // anonymous namespace
+
+
+namespace Java {
+
+class ClassCacheImpl
+{
+
+ friend class ClassCache;
+
+ // We only statically initialize a few of the common class wrappers.
+ explicit ClassCacheImpl(Env env) :
+
+#define JNIWRAPPER_INIT_CACHED_CLASS(M, C) \
+ m_impl_##M(new C::ClassImpl(env, env.FindClass(C::m_class_name)))
+
+ JNIWRAPPER_INIT_CACHED_CLASS(object, Object),
+ JNIWRAPPER_INIT_CACHED_CLASS(classtype, Class),
+ JNIWRAPPER_INIT_CACHED_CLASS(throwable, Exception),
+ JNIWRAPPER_INIT_CACHED_CLASS(string, String)
+#undef JNIWRAPPER_INIT_CACHED_CLASS
+ {}
+
+ // We can't do this in the constructor above, because the satic
+ // initializers will expect that ClassCache::m_impl is already set;
+ // that doesn't happen until the constructor returns.
+ void static_init(Env env)
+ {
+#define JNIWRAPPER_STATIC_CACHED_CLASS(M, C) \
+ C::static_init(env, m_impl_##M->get_class())
+
+ // No-op JNIWRAPPER_STATIC_CACHED_CLASS(object, Object);
+ JNIWRAPPER_STATIC_CACHED_CLASS(classtype, Class);
+ JNIWRAPPER_STATIC_CACHED_CLASS(throwable, Exception);
+ // No-op JNIWRAPPER_STATIC_CACHED_CLASS(string, String);
+#undef JNIWRAPPER_STATIC_CACHED_CLASS
+ }
+
+ // The statically initialized calss wrappers are always defined and
+ // therefore do not need atomic access.
+#define JNIWRAPPER_DEFINE_CACHED_CLASS(M, C) \
+ std::auto_ptr<Object::ClassImpl> m_impl_##M; \
+ const Object::ClassImpl* get_##M(Env) \
+ { \
+ return m_impl_##M.get(); \
+ }
+
+ JNIWRAPPER_DEFINE_CACHED_CLASS(object, Object)
+ JNIWRAPPER_DEFINE_CACHED_CLASS(classtype, Class)
+ JNIWRAPPER_DEFINE_CACHED_CLASS(throwable, Exception)
+ JNIWRAPPER_DEFINE_CACHED_CLASS(string, String)
+#undef JNIWRAPPER_DEFINE_CACHED_CLASS
+
+ // All other class wrappers must be atomically initialized
+#define JNIWRAPPER_DEFINE_CACHED_CLASS(M, C) \
+ ClassImplPtr m_impl_##M; \
+ const Object::ClassImpl* get_##M(Env env) \
+ { \
+ Object::ClassImpl* pimpl = m_impl_##M.get(); \
+ if (!pimpl) \
+ { \
+ std::auto_ptr<Object::ClassImpl> tmp( \
+ new C::ClassImpl( \
+ env, env.FindClass(C::m_class_name))); \
+ pimpl = m_impl_##M.test_and_set(tmp.get()); \
+ if (!pimpl) \
+ pimpl = tmp.release(); \
+ } \
+ return pimpl; \
+ }
+
+ JNIWRAPPER_DEFINE_CACHED_CLASS(exc_index_out_of_bounds,
+ IndexOutOfBoundsException);
+ JNIWRAPPER_DEFINE_CACHED_CLASS(exc_no_such_element,
+ NoSuchElementException);
+
+ JNIWRAPPER_DEFINE_CACHED_CLASS(iterator, BaseIterator);
+
+ JNIWRAPPER_DEFINE_CACHED_CLASS(list, BaseImmutableList);
+ JNIWRAPPER_DEFINE_CACHED_CLASS(array_list, BaseList);
+
+ JNIWRAPPER_DEFINE_CACHED_CLASS(map, BaseImmutableMap);
+ JNIWRAPPER_DEFINE_CACHED_CLASS(set, BaseImmutableMap::Set);
+ JNIWRAPPER_DEFINE_CACHED_CLASS(map_entry, BaseImmutableMap::Entry);
+ JNIWRAPPER_DEFINE_CACHED_CLASS(hash_map, BaseMap);
+
+ JNIWRAPPER_DEFINE_CACHED_CLASS(input_stream, InputStream);
+ JNIWRAPPER_DEFINE_CACHED_CLASS(output_stream, OutputStream);
+
+ JNIWRAPPER_DEFINE_CACHED_CLASS(byte_buffer,
+ ByteChannel::ByteBuffer);
+
+ JNIWRAPPER_DEFINE_CACHED_CLASS(subversion_exception,
+ ::JavaHL::SubversionException);
+
+ JNIWRAPPER_DEFINE_CACHED_CLASS(authn_cb,
+ ::JavaHL::AuthnCallback);
+ JNIWRAPPER_DEFINE_CACHED_CLASS(authn_result,
+ ::JavaHL::AuthnCallback::AuthnResult);
+ JNIWRAPPER_DEFINE_CACHED_CLASS(authn_ssl_server_cert_failures,
+ ::JavaHL::AuthnCallback::SSLServerCertFailures);
+ JNIWRAPPER_DEFINE_CACHED_CLASS(authn_ssl_server_cert_info,
+ ::JavaHL::AuthnCallback::SSLServerCertInfo);
+ JNIWRAPPER_DEFINE_CACHED_CLASS(user_passwd_cb,
+ ::JavaHL::UserPasswordCallback);
+
+ JNIWRAPPER_DEFINE_CACHED_CLASS(credential,
+ ::JavaHL::Credential);
+ JNIWRAPPER_DEFINE_CACHED_CLASS(credential_kind,
+ ::JavaHL::Credential::Kind);
+
+ JNIWRAPPER_DEFINE_CACHED_CLASS(external_item,
+ ::JavaHL::ExternalItem);
+
+ JNIWRAPPER_DEFINE_CACHED_CLASS(editor_provide_base_cb,
+ ::JavaHL::ProvideBaseCallback);
+ JNIWRAPPER_DEFINE_CACHED_CLASS(editor_provide_base_cb_ret,
+ ::JavaHL::ProvideBaseCallback::ReturnValue);
+ JNIWRAPPER_DEFINE_CACHED_CLASS(editor_provide_props_cb,
+ ::JavaHL::ProvidePropsCallback);
+ JNIWRAPPER_DEFINE_CACHED_CLASS(editor_provide_props_cb_ret,
+ ::JavaHL::ProvidePropsCallback::ReturnValue);
+ JNIWRAPPER_DEFINE_CACHED_CLASS(editor_get_kind_cb,
+ ::JavaHL::GetNodeKindCallback);
+#undef JNIWRAPPER_DEFINE_CACHED_CLASS
+};
+
+
+ClassCacheImpl* ClassCache::m_impl = NULL;
+
+void ClassCache::create()
+{
+ const char* exception_message = NULL;
+
+ try
+ {
+ const Env env;
+ m_impl = new ClassCacheImpl(env);
+ m_impl->static_init(env);
+ }
+ catch (const SignalExceptionThrown&)
+ {}
+ catch (const std::exception& ex)
+ {
+ exception_message = ex.what();
+ }
+ catch (...)
+ {
+ exception_message = "Caught unknown C++ exception";
+ }
+
+ // Do not throw any more exceptions from here, so use the raw environment.
+ ::JNIEnv* const jenv = Env().get();
+ if (exception_message || jenv->ExceptionCheck())
+ {
+ jobject cause = jenv->ExceptionOccurred();
+ if (cause)
+ jenv->ExceptionClear();
+
+ const jclass rtx = jenv->FindClass("java/lang/RuntimeException");
+ const jmethodID ctor = jenv->GetMethodID(rtx, "<init>",
+ "(Ljava/lang/String;"
+ "Ljava/lang/Throwable;)V");
+ 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()
+{
+ ClassCacheImpl* const pimpl = m_impl;
+ m_impl = NULL;
+ delete pimpl;
+}
+
+#define JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(M) \
+const Object::ClassImpl* ClassCache::get_##M(Env env) \
+{ \
+ return m_impl->get_##M(env); \
+}
+
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(object);
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(classtype);
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(throwable);
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(string);
+
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(exc_index_out_of_bounds);
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(exc_no_such_element);
+
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(list);
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(array_list);
+
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(map);
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(set);
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(iterator);
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(map_entry);
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(hash_map);
+
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(input_stream);
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(output_stream);
+
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(byte_buffer);
+
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(subversion_exception);
+
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(authn_cb);
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(authn_result);
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(authn_ssl_server_cert_failures);
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(authn_ssl_server_cert_info);
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(user_passwd_cb);
+
+
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(credential);
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(credential_kind);
+
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(external_item);
+
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(editor_provide_base_cb);
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(editor_provide_base_cb_ret);
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(editor_provide_props_cb);
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(editor_provide_props_cb_ret);
+JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(editor_get_kind_cb);
+#undef JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR
+
+} // namespace Java
diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_env.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_env.hpp
new file mode 100644
index 0000000..9d73653
--- /dev/null
+++ b/subversion/bindings/javahl/native/jniwrapper/jni_env.hpp
@@ -0,0 +1,682 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#ifndef SVN_JAVAHL_JNIWRAPPER_ENV_HPP
+#define SVN_JAVAHL_JNIWRAPPER_ENV_HPP
+
+#include <jni.h>
+#include <cstdarg>
+#include <stdexcept>
+
+#ifdef SVN_JAVAHL_DEBUG
+# 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)
+#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.
+ *
+ * @since New in 1.9.
+ */
+class MethodID
+{
+public:
+ MethodID()
+ : m_mid(NULL)
+ {}
+
+ MethodID(jmethodID mid)
+ : m_mid(mid)
+ {}
+
+ MethodID(const MethodID& that)
+ : m_mid(that.m_mid)
+ {}
+
+ MethodID& operator=(jmethodID mid)
+ {
+ m_mid = mid;
+ return *this;
+ }
+
+ MethodID& operator=(const MethodID& that)
+ {
+ m_mid = that.m_mid;
+ return *this;
+ }
+
+ operator jmethodID() const
+ {
+ return m_mid;
+ }
+
+ operator bool() const
+ {
+ return (NULL != m_mid);
+ }
+
+private:
+ jmethodID m_mid;
+};
+
+
+/**
+ * Auto-initializing proxy for the JNI field ID.
+ *
+ * Behaves like a @c jfieldID but automatically initializes to @c NULL.
+ *
+ * @since New in 1.9.
+ */
+class FieldID
+{
+public:
+ FieldID()
+ : m_fid(NULL)
+ {}
+
+ FieldID(jfieldID mid)
+ : m_fid(mid)
+ {}
+
+ FieldID(const FieldID& that)
+ : m_fid(that.m_fid)
+ {}
+
+ FieldID& operator=(jfieldID fid)
+ {
+ m_fid = fid;
+ return *this;
+ }
+
+ FieldID& operator=(const FieldID& that)
+ {
+ m_fid = that.m_fid;
+ return *this;
+ }
+
+ operator jfieldID() const
+ {
+ return m_fid;
+ }
+
+ operator bool() const
+ {
+ return (NULL != m_fid);
+ }
+
+private:
+ jfieldID m_fid;
+};
+
+/**
+ * Encapsulation of a JNI environment reference.
+ *
+ * This class wraps all (relevant) JNI functions and checks for thrown
+ * exceptions, so that call sites don't have to be cluttered with KNI
+ * exception checks.
+ *
+ * @since New in 1.9.
+ */
+class Env
+{
+public:
+ /**
+ * Constructs an environment object, retrieving the JNI environment
+ * reference from the global JVM reference.
+ */
+ explicit Env()
+ : m_env(env_from_jvm())
+ {}
+
+ /**
+ * Given a JNI renvironment reference, constructs an environment object.
+ */
+ explicit Env(::JNIEnv* env)
+ : m_env(env)
+ {}
+
+ /**
+ * Returns the wrapped JNI environment reference.
+ *
+ * This method is present for compatibility with the old-style
+ * native implmentation that needs the raw pointer, and will be
+ * removed presently. Do not use it in new-style code.
+ */
+ ::JNIEnv* get() const
+ {
+ SVN_JAVAHL_JNIWRAPPER_LOG("Warning: Direct access to JNIEnv at "
+ << __FILE__ << ":" << __LINE__);
+ return m_env;
+ }
+
+ /** Wrapped JNI function. */
+ jobject NewGlobalRef(jobject obj) const
+ {
+ jobject ret = m_env->NewGlobalRef(obj);
+ check_java_exception();
+ if (!ret)
+ throw_java_out_of_memory(error_create_global_reference());
+ return ret;
+ }
+
+ /** Wrapped JNI function. */
+ void DeleteGlobalRef(jobject obj) const throw()
+ {
+ m_env->DeleteGlobalRef(obj);
+ }
+
+ /** Wrapped JNI function. */
+ void PushLocalFrame(jint capacity) const
+ {
+ if (0 > m_env->PushLocalFrame(capacity))
+ throw_java_exception();
+ }
+
+ /** Wrapped JNI function. */
+ void PopLocalFrame() const throw()
+ {
+ m_env->PopLocalFrame(NULL);
+ }
+
+ /** Wrapped JNI function. */
+ jint Throw(jthrowable exc) const throw()
+ {
+ return m_env->Throw(exc);
+ }
+
+ /** Wrapped JNI function. */
+ 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);
+ check_java_exception();
+ return cls;
+ }
+
+ /** Wrapped JNI function. */
+ jobject NewObject(jclass cls, jmethodID ctor, ...) const
+ {
+ std::va_list args;
+ va_start(args, ctor);
+ jobject obj = m_env->NewObjectV(cls, ctor, args);
+ va_end(args);
+ check_java_exception();
+ return obj;
+ }
+
+ /** Wrapped JNI function. */
+ jclass GetObjectClass(jobject obj) const
+ {
+ jclass cls = m_env->GetObjectClass(obj);
+ check_java_exception();
+ return cls;
+ }
+
+ /** Wrapped JNI function. */
+ jboolean IsInstanceOf(jobject obj, jclass cls) const throw()
+ {
+ 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);
+ check_java_exception();
+ return mid;
+ }
+
+ /** Wrapped JNI function. */
+ jmethodID GetStaticMethodID(jclass cls, const char* name,
+ const char* sig) const
+ {
+ jmethodID mid = m_env->GetStaticMethodID(cls, name, sig);
+ check_java_exception();
+ return mid;
+ }
+
+ /** Wrapped JNI function. */
+ jfieldID GetFieldID(jclass cls, const char* name, const char* sig) const
+ {
+ jfieldID fid = m_env->GetFieldID(cls, name, sig);
+ check_java_exception();
+ return fid;
+ }
+
+ /** Wrapped JNI function. */
+ jfieldID GetStaticFieldID(jclass cls, const char* name,
+ const char* sig) const
+ {
+ jfieldID fid = m_env->GetStaticFieldID(cls, name, sig);
+ check_java_exception();
+ return fid;
+ }
+
+ /** Wrapped JNI function. */
+ jstring NewStringUTF(const char* text) const
+ {
+ if (!text)
+ return NULL;
+
+ jstring str = m_env->NewStringUTF(text);
+ check_java_exception();
+ return str;
+ }
+
+ /** Wrapped JNI function. */
+ jsize GetStringLength(jstring str) const
+ {
+ jsize len = m_env->GetStringLength(str);
+ check_java_exception();
+ return len;
+ }
+
+ /** Wrapped JNI function. */
+ jsize GetStringUTFLength(jstring str) const
+ {
+ jsize len = m_env->GetStringUTFLength(str);
+ check_java_exception();
+ return len;
+ }
+
+ /** Wrapped JNI function. */
+ const char* GetStringUTFChars(jstring str, jboolean* is_copy) const
+ {
+ if (!str)
+ return NULL;
+
+ const char* text = m_env->GetStringUTFChars(str, is_copy);
+ check_java_exception();
+ if (!text)
+ throw_java_out_of_memory(error_get_contents_string());
+ return text;
+ }
+
+ /** Wrapped JNI function. */
+ void ReleaseStringUTFChars(jstring str, const char* new_text) const
+ {
+ if (!str)
+ throw std::logic_error(error_release_null_string());
+ m_env->ReleaseStringUTFChars(str, new_text);
+ }
+
+ /** Wrapped JNI function. */
+ void CallVoidMethod(jobject obj, jmethodID mid, ...) const
+ {
+ std::va_list args;
+ va_start(args, mid);
+ m_env->CallObjectMethodV(obj, mid, args);
+ va_end(args);
+ check_java_exception();
+ }
+
+ /** Boilerplate generator for wrapped JNI functions. */
+#define SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD(T, N) \
+ T Call##N##Method(jobject obj, jmethodID mid, ...) const \
+ { \
+ std::va_list args; \
+ va_start(args, mid); \
+ T ret = m_env->Call##N##MethodV(obj, mid, args); \
+ va_end(args); \
+ check_java_exception(); \
+ return ret; \
+ }
+ SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD(jobject, Object)
+ SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD(jboolean, Boolean)
+ SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD(jbyte, Byte)
+ SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD(jchar, Char)
+ SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD(jshort, Short)
+ SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD(jint, Int)
+ SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD(jlong, Long)
+ SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD(jfloat, Float)
+ SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD(jdouble, Double)
+#undef SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD
+
+ /** Boilerplate generator for wrapped JNI functions. */
+#define SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD(T, N) \
+ T CallStatic##N##Method(jclass obj, jmethodID mid, ...) const \
+ { \
+ std::va_list args; \
+ va_start(args, mid); \
+ T ret = m_env->CallStatic##N##MethodV(obj, mid, args); \
+ va_end(args); \
+ check_java_exception(); \
+ return ret; \
+ }
+ SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD(jobject, Object)
+ SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD(jboolean, Boolean)
+ SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD(jbyte, Byte)
+ SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD(jchar, Char)
+ SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD(jshort, Short)
+ SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD(jint, Int)
+ SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD(jlong, Long)
+ SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD(jfloat, Float)
+ SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD(jdouble, Double)
+#undef SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD
+
+ /** Boilerplate generator for wrapped JNI functions. */
+#define SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD(T, N) \
+ T Get##N##Field(jobject obj, jfieldID fid) const \
+ { \
+ T ret = m_env->Get##N##Field(obj, fid); \
+ check_java_exception(); \
+ return ret; \
+ }
+ SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD(jobject, Object)
+ SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD(jboolean, Boolean)
+ SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD(jbyte, Byte)
+ SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD(jchar, Char)
+ SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD(jshort, Short)
+ SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD(jint, Int)
+ SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD(jlong, Long)
+ SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD(jfloat, Float)
+ SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD(jdouble, Double)
+#undef SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD
+
+ /** Boilerplate generator for wrapped JNI functions. */
+#define SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD(T, N) \
+ void Set##N##Field(jobject obj, jfieldID fid, T val) const \
+ { \
+ m_env->Set##N##Field(obj, fid, val); \
+ check_java_exception(); \
+ }
+ SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD(jobject, Object)
+ SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD(jboolean, Boolean)
+ SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD(jbyte, Byte)
+ SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD(jchar, Char)
+ SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD(jshort, Short)
+ SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD(jint, Int)
+ SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD(jlong, Long)
+ SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD(jfloat, Float)
+ SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD(jdouble, Double)
+#undef SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD
+
+ /** Boilerplate generator for wrapped JNI functions. */
+#define SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD(T, N) \
+ T GetStatic##N##Field(jclass cls, jfieldID fid) const \
+ { \
+ T ret = m_env->GetStatic##N##Field(cls, fid); \
+ check_java_exception(); \
+ return ret; \
+ }
+ SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD(jobject, Object)
+ SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD(jboolean, Boolean)
+ SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD(jbyte, Byte)
+ SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD(jchar, Char)
+ SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD(jshort, Short)
+ SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD(jint, Int)
+ SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD(jlong, Long)
+ SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD(jfloat, Float)
+ SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD(jdouble, Double)
+#undef SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD
+
+ /** Boilerplate generator for wrapped JNI functions. */
+#define SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD(T, N) \
+ void SetStatic##N##Field(jclass cls, jfieldID fid, T val) const \
+ { \
+ m_env->SetStatic##N##Field(cls, fid, val); \
+ check_java_exception(); \
+ }
+ SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD(jobject, Object)
+ SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD(jboolean, Boolean)
+ SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD(jbyte, Byte)
+ SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD(jchar, Char)
+ SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD(jshort, Short)
+ SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD(jint, Int)
+ SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD(jlong, Long)
+ SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD(jfloat, Float)
+ SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD(jdouble, Double)
+#undef SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD
+
+ /** Wrapped JNI function. */
+ jsize GetArrayLength(jarray array) const
+ {
+ if (!array)
+ return 0;
+ return m_env->GetArrayLength(array);
+ }
+
+ /** Wrapped JNI function. */
+ jobjectArray NewObjectArray(jsize length, jclass cls, jobject init) const
+ {
+ jobjectArray array = m_env->NewObjectArray(length, cls, init);
+ if (!array)
+ throw_java_out_of_memory(error_create_object_array());
+ return array;
+ }
+
+ /** Wrapped JNI function. */
+ jobject GetObjectArrayElement(jobjectArray array, jsize index) const
+ {
+ jobject obj = m_env->GetObjectArrayElement(array, index);
+ check_java_exception();
+ return obj;
+ }
+
+ /** Wrapped JNI function. */
+ void SetObjectArrayElement(jobjectArray array,
+ jsize index, jobject value) const
+ {
+ m_env->SetObjectArrayElement(array, index, value);
+ check_java_exception();
+ }
+
+ /** Boilerplate generator for wrapped JNI functions. */
+#define SVN_JAVAHL_JNIWRAPPER_PRIMITIVE_TYPE_ARRAY(T, N) \
+ T##Array New##N##Array(jsize length) const \
+ { \
+ T##Array array = m_env->New##N##Array(length); \
+ if (!array) \
+ throw_java_out_of_memory(error_create_array(#T)); \
+ return array; \
+ } \
+ T* Get##N##ArrayElements(T##Array array, jboolean* is_copy) const \
+ { \
+ if (!array) \
+ return NULL; \
+ \
+ T* data = m_env->Get##N##ArrayElements(array, is_copy); \
+ check_java_exception(); \
+ if (!data) \
+ throw_java_out_of_memory(error_get_contents_array(#N)); \
+ return data; \
+ } \
+ void Release##N##ArrayElements(T##Array array, T* data, jint mode) const \
+ { \
+ if (!array) \
+ throw std::logic_error(error_release_null_array(#T)); \
+ m_env->Release##N##ArrayElements(array, data, mode); \
+ }
+
+ SVN_JAVAHL_JNIWRAPPER_PRIMITIVE_TYPE_ARRAY(jboolean, Boolean)
+ SVN_JAVAHL_JNIWRAPPER_PRIMITIVE_TYPE_ARRAY(jbyte, Byte)
+ SVN_JAVAHL_JNIWRAPPER_PRIMITIVE_TYPE_ARRAY(jchar, Char)
+ SVN_JAVAHL_JNIWRAPPER_PRIMITIVE_TYPE_ARRAY(jshort, Short)
+ SVN_JAVAHL_JNIWRAPPER_PRIMITIVE_TYPE_ARRAY(jint, Int)
+ SVN_JAVAHL_JNIWRAPPER_PRIMITIVE_TYPE_ARRAY(jlong, Long)
+ SVN_JAVAHL_JNIWRAPPER_PRIMITIVE_TYPE_ARRAY(jfloat, Float)
+ SVN_JAVAHL_JNIWRAPPER_PRIMITIVE_TYPE_ARRAY(jdouble, Double)
+#undef SVN_JAVAHL_JNIWRAPPER_PRIMITIVE_TYPE_ARRAY
+
+ /** Wrapped JNI function. */
+ void* GetDirectBufferAddress(jobject buffer) const
+ {
+ void* const addr = m_env->GetDirectBufferAddress(buffer);
+ check_java_exception();
+ return addr;
+ }
+
+private:
+ ::JNIEnv* m_env;
+ static ::JavaVM* m_jvm;
+ static ::JNIEnv* env_from_jvm();
+
+ void throw_java_exception() const
+ {
+ throw SignalExceptionThrown();
+ }
+
+ void check_java_exception() const
+ {
+ if (m_env->ExceptionCheck())
+ throw SignalExceptionThrown();
+ }
+
+ void throw_java_out_of_memory(const char* message) const;
+
+ // We cannont use svn_private_config.h in a header, so we move the
+ // actual message translations into the implementation file.
+ static const char* error_create_global_reference() throw();
+ static const char* error_get_contents_string() throw();
+ static const char* error_release_null_string() throw();
+
+ static const char* error_create_object_array() throw();
+ static const char* error_create_array(const char* type) throw();
+ static const char* error_get_contents_array(const char* type) throw();
+ static const char* error_release_null_array(const char* type) throw();
+
+public:
+ // This static initializer must only be called by JNI_OnLoad
+ static void static_init(::JavaVM*);
+};
+
+
+/**
+ * Encapsulation of a JNI local frame.
+ *
+ * Used within loop bodies to limit the proliferation of local
+ * references, or anywhere else where such references should be
+ * pre-emptively discarded.
+ *
+ * @since New in 1.9.
+ */
+class LocalFrame
+{
+ static const jint DEFAULT_CAPACITY;
+
+public:
+ /**
+ * Constructs a local frame, retrieving the JNI environment
+ * reference from the global JVM reference.
+ */
+ explicit LocalFrame()
+ : m_env(Env())
+ {
+ m_env.PushLocalFrame(DEFAULT_CAPACITY);
+ }
+
+ /**
+ * Given a JNI renvironment reference, constructs a local frame.
+ */
+ explicit LocalFrame(Env env)
+ : m_env(env)
+ {
+ m_env.PushLocalFrame(DEFAULT_CAPACITY);
+ }
+
+ /**
+ * Constructs a local frame with the given initial @a capacity,
+ * retrieving the JNI environment reference from the global JVM
+ * reference.
+ */
+ explicit LocalFrame(jint capacity)
+ : m_env(Env())
+ {
+ m_env.PushLocalFrame(capacity);
+ }
+
+ /**
+ * Given a JNI renvironment reference, constructs a local frame with
+ * the given initial @a capacity.
+ */
+ explicit LocalFrame(Env env, jint capacity)
+ : m_env(env)
+ {
+ m_env.PushLocalFrame(capacity);
+ }
+
+ ~LocalFrame()
+ {
+ m_env.PopLocalFrame();
+ }
+
+ /**
+ * Returns the stored enviromnent object.
+ */
+ Env get_env() const
+ {
+ return m_env;
+ }
+
+private:
+ const Env m_env;
+ LocalFrame(const LocalFrame&);
+ LocalFrame& operator=(const LocalFrame&);
+};
+
+} // namespace Java
+
+#endif // SVN_JAVAHL_JNIWRAPPER_ENV_HPP
diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp
new file mode 100644
index 0000000..2062275
--- /dev/null
+++ b/subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp
@@ -0,0 +1,364 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+
+#ifndef SVN_JAVAHL_JNIWRAPPER_EXCEPTION_HPP
+#define SVN_JAVAHL_JNIWRAPPER_EXCEPTION_HPP
+
+#include "jni_env.hpp"
+#include "jni_object.hpp"
+
+namespace Java {
+
+/**
+ * Base class for all exception generators, and generator class for
+ * exceptions of type @c java.lang.Throwable.
+ *
+ * The associated JNI class reference is stored for the lifetime of
+ * the JVM in the global class cache.
+ *
+ * @since New in 1.9.
+ */
+class Exception
+{
+public:
+ /**
+ * Constructs a wrapper for the @c jthrowable object @a exc.
+ */
+ explicit Exception(Env env, jthrowable exc)
+ : m_env(env),
+ m_jthis(exc),
+ m_class(env.GetObjectClass(exc))
+ {}
+
+ /**
+ * Raises a Java exception of the concrete class, and throws a
+ * native exception at the same time.
+ *
+ * It is an error to call this method if an existing @c jthrowable
+ * object was wrapped.
+ */
+ void raise() const
+ {
+ throw_java_exception();
+ throw SignalExceptionThrown();
+ }
+
+ /**
+ * Raises a Java exception of the concrete class with the givem
+ * @a message, and throws a native exception at the same time.
+ *
+ * It is an error to call this method if an existing @c jthrowable
+ * object was wrapped.
+ */
+ void raise(const char* message) const
+ {
+ throw_java_exception(message);
+ throw SignalExceptionThrown();
+ }
+
+ /**
+ * Raises a Java exception of the concrete class, but does not throw
+ * a native exception.
+ *
+ * It is an error to call this method if an existing @c jthrowable
+ * object was wrapped.
+ */
+ void throw_java_exception() const;
+
+ /**
+ * Raises a Java exception of the concrete class with the given
+ * @a message, but does not throw a native exception.
+ *
+ * It is an error to call this method if an existing @c jthrowable
+ * object was wrapped.
+ */
+ void throw_java_exception(const char* message) const;
+
+ /**
+ * Checks if an existing @c jthrowable object was wrapped.
+ */
+ bool instantiated() const
+ {
+ return (m_jthis != NULL);
+ }
+
+ /**
+ * Returns the wrapped @c jthrowable object.
+ */
+ jthrowable throwable() const
+ {
+ return m_jthis;
+ }
+
+ /**
+ * Wrapper for the Java method @c getMessage().
+ * Only valid if an existing @c jthrowable object was wrapped.
+ */
+ jstring get_message() const;
+
+
+ /**
+ * Returns the wrapped exception instance.
+ */
+ jobject get() const
+ {
+ return m_jthis;
+ }
+
+ /**
+ * Returns the wrapped exception class.
+ */
+ jclass get_class() const
+ {
+ return m_class;
+ }
+
+ /**
+ * Returns the wrapped enviromnment reference.
+ */
+ Env get_env() const
+ {
+ return m_env;
+ }
+
+protected:
+ /**
+ * Constructs an exception generator with the concrete class
+ * @a class_name.
+ */
+ explicit Exception(Env env, const char* class_name)
+ : m_env(env),
+ m_jthis(NULL),
+ m_class(env.FindClass(class_name))
+ {}
+
+ /**
+ * Constructs an exception generator with the concrete class @a cls.
+ */
+ explicit Exception(Env env, const Object::ClassImpl* impl)
+ : m_env(env),
+ m_jthis(NULL),
+ m_class(impl->get_class())
+ {}
+
+private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(Env env, jclass cls)
+ : Object::ClassImpl(env, cls)
+ {}
+
+ public:
+ virtual ~ClassImpl();
+ };
+
+ const Env m_env;
+ jthrowable m_jthis;
+ jclass m_class;
+
+ friend class ClassCacheImpl;
+ static void static_init(Env env, jclass cls);
+ static const char* const m_class_name;
+ static MethodID m_mid_get_message;
+};
+
+/**
+ * Generator class for exceptions of type @c java.lang.RuntimeException.
+ *
+ * @since New in 1.9.
+ */
+class RuntimeException : public Exception
+{
+public:
+ /**
+ * Constructs an exception generator object.
+ */
+ explicit RuntimeException(Env env)
+ : Exception(env, m_class_name)
+ {}
+
+private:
+ static const char* const m_class_name;
+};
+
+
+/**
+ * Generator class for exceptions of type @c java.lang.NullPointerException.
+ *
+ * @since New in 1.9.
+ */
+class NullPointerException : public Exception
+{
+public:
+ /**
+ * Constructs an exception generator object.
+ */
+ explicit NullPointerException(Env env)
+ : Exception(env, m_class_name)
+ {}
+
+private:
+ static const char* const m_class_name;
+};
+
+/**
+ * Generator class for exceptions of type @c java.lang.OutOfMemoryError.
+ *
+ * @since New in 1.9.
+ */
+class OutOfMemoryError : public Exception
+{
+public:
+ /**
+ * Constructs an exception generator object.
+ */
+ explicit OutOfMemoryError(Env env)
+ : Exception(env, m_class_name)
+ {}
+
+private:
+ static const char* const m_class_name;
+};
+
+/**
+ * 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:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(Env env, jclass cls)
+ : Object::ClassImpl(env, cls)
+ {}
+
+ public:
+ virtual ~ClassImpl();
+ };
+
+ friend class ClassCacheImpl;
+ static const char* const m_class_name;
+};
+
+/**
+ * Generator class for exceptions of type @c java.io.IOException.
+ *
+ * @since New in 1.9.
+ */
+class IOException : public Exception
+{
+public:
+ /**
+ * Constructs an exception generator object.
+ */
+ explicit IOException(Env env)
+ : Exception(env, m_class_name)
+ {}
+
+private:
+ static const char* const m_class_name;
+};
+
+/**
+ * Generator class for exceptions of type @c java.lang.IllegalArgumentException.
+ *
+ * @since New in 1.9.
+ */
+class IllegalArgumentException : public Exception
+{
+public:
+ /**
+ * Constructs an exception generator object.
+ */
+ explicit IllegalArgumentException(Env env)
+ : Exception(env, m_class_name)
+ {}
+
+private:
+ static const char* const m_class_name;
+};
+
+/**
+ * Generator class for exceptions of type
+ * @c java.util.NoSuchElementException.
+ *
+ * @since New in 1.9.
+ */
+class NoSuchElementException : public Exception
+{
+public:
+ /**
+ * Constructs an exception generator object.
+ */
+ explicit NoSuchElementException(Env env)
+ : Exception(env, m_class_name)
+ {}
+
+private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(Env env, jclass cls)
+ : Object::ClassImpl(env, cls)
+ {}
+
+ public:
+ virtual ~ClassImpl();
+ };
+
+ friend class ClassCacheImpl;
+ static const char* const m_class_name;
+};
+
+} // namespace Java
+
+#endif // SVN_JAVAHL_JNIWRAPPER_EXCEPTION_HPP
diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp
new file mode 100644
index 0000000..7fe88d0
--- /dev/null
+++ b/subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp
@@ -0,0 +1,90 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#ifndef SVN_JAVAHL_JNIWRAPPER_GLOBALREF_HPP
+#define SVN_JAVAHL_JNIWRAPPER_GLOBALREF_HPP
+
+#include <jni.h>
+
+#include "jni_env.hpp"
+
+namespace Java {
+
+/**
+ * Wrapper for a global object reference. The reference is held until
+ * the wrapper goes out of scope (i.e., until the destructor is called).
+ *
+ * @since New in 1.9.
+ */
+class GlobalObject
+{
+public:
+ explicit GlobalObject(Env env, jobject obj)
+ : m_obj(obj ? env.NewGlobalRef(obj) : NULL)
+ {}
+
+ ~GlobalObject();
+
+ GlobalObject& operator=(jobject that);
+
+ jobject get() const
+ {
+ return m_obj;
+ }
+
+private:
+ GlobalObject(const GlobalObject&);
+ GlobalObject& operator=(const GlobalObject&);
+
+ jobject m_obj;
+};
+
+/**
+ * Wrapper for a global class reference. Behaves just like the object
+ * reference wrapper, but provides a more type-safe interface for
+ * class references.
+ *
+ * @since New in 1.9.
+ */
+class GlobalClass : protected GlobalObject
+{
+public:
+ explicit GlobalClass(Env env, jclass cls)
+ : GlobalObject(env, cls)
+ {}
+
+ GlobalClass& operator=(jclass that);
+
+ jclass get() const
+ {
+ return jclass(GlobalObject::get());
+ }
+
+private:
+ GlobalClass(const GlobalClass&);
+ GlobalClass& operator=(const GlobalClass&);
+};
+
+} // namespace Java
+
+#endif // SVN_JAVAHL_JNIWRAPPER_GLOBALREF_HPP
diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_io_stream.cpp b/subversion/bindings/javahl/native/jniwrapper/jni_io_stream.cpp
new file mode 100644
index 0000000..3c055ca
--- /dev/null
+++ b/subversion/bindings/javahl/native/jniwrapper/jni_io_stream.cpp
@@ -0,0 +1,294 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#include "jni_globalref.hpp"
+#include "jni_io_stream.hpp"
+#include "jni_stack.hpp"
+
+#include "svn_private_config.h"
+
+// Stream-wrapper-specific mark object type
+struct svn_stream_mark_t
+{
+ void* m_baton;
+};
+
+namespace Java {
+
+namespace {
+svn_error_t* stream_close_input(void* baton)
+{
+ InputStream* const self = static_cast<InputStream*>(baton);
+ SVN_JAVAHL_CATCH(self->get_env(), SVN_ERR_BASE, self->close());
+ return SVN_NO_ERROR;
+}
+
+svn_error_t* stream_mark(void* baton, svn_stream_mark_t** mark,
+ apr_pool_t* result_pool)
+{
+ InputStream* const self = static_cast<InputStream*>(baton);
+ SVN_JAVAHL_CATCH(self->get_env(), SVN_ERR_STREAM_SEEK_NOT_SUPPORTED,
+ self->mark(16384)); // FIXME: invent better readlimit
+
+ *mark = static_cast<svn_stream_mark_t*>(
+ apr_palloc(result_pool, sizeof(**mark)));
+ (*mark)->m_baton = baton;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t* stream_seek(void* baton, const svn_stream_mark_t* mark)
+{
+ if (mark->m_baton != baton)
+ return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED,
+ NULL, _("Invalid mark"));
+
+ InputStream* const self = static_cast<InputStream*>(baton);
+ SVN_JAVAHL_CATCH(self->get_env(), SVN_ERR_STREAM_SEEK_NOT_SUPPORTED,
+ self->reset());
+ return SVN_NO_ERROR;
+}
+
+svn_error_t* stream_read(void* baton, char* buffer, apr_size_t* len)
+{
+ if (0 == *len)
+ return SVN_NO_ERROR;
+
+ jint length = jint(*len);
+ InputStream* const self = static_cast<InputStream*>(baton);
+ SVN_JAVAHL_CATCH(self->get_env(), SVN_ERR_BASE,
+ length = self->read(buffer, length));
+ if (length < 0)
+ *len = 0;
+ else
+ *len = length;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t* stream_skip(void* baton, apr_size_t len)
+{
+ InputStream* const self = static_cast<InputStream*>(baton);
+ SVN_JAVAHL_CATCH(self->get_env(), SVN_ERR_BASE, self->skip(jlong(len)));
+ return SVN_NO_ERROR;
+}
+
+svn_error_t* stream_close_output(void* baton)
+{
+ OutputStream* const self = static_cast<OutputStream*>(baton);
+ SVN_JAVAHL_CATCH(self->get_env(), SVN_ERR_BASE, self->close());
+ return SVN_NO_ERROR;
+}
+
+svn_error_t* stream_write(void* baton, const char* data, apr_size_t* len)
+{
+ OutputStream* const self = static_cast<OutputStream*>(baton);
+ SVN_JAVAHL_CATCH(self->get_env(), SVN_ERR_BASE,
+ self->write(data, jint(*len)));
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t*
+global_stream_close_input(void* baton)
+{
+ GlobalObject* ref = static_cast<GlobalObject*>(baton);
+ InputStream stream(Env(), ref->get());
+ return stream_close_input(&stream);
+}
+
+svn_error_t*
+global_stream_mark(void* baton, svn_stream_mark_t** mark,
+ apr_pool_t* result_pool)
+{
+ GlobalObject* ref = static_cast<GlobalObject*>(baton);
+ InputStream stream(Env(), ref->get());
+ return stream_mark(&stream, mark, result_pool);
+}
+
+svn_error_t*
+global_stream_seek(void* baton, const svn_stream_mark_t* mark)
+{
+ GlobalObject* ref = static_cast<GlobalObject*>(baton);
+ InputStream stream(Env(), ref->get());
+ return stream_seek(&stream, mark);
+}
+
+svn_error_t*
+global_stream_read(void* baton, char* buffer, apr_size_t* len)
+{
+ GlobalObject* ref = static_cast<GlobalObject*>(baton);
+ InputStream stream(Env(), ref->get());
+ return stream_read(&stream, buffer, len);
+}
+
+svn_error_t*
+global_stream_skip(void* baton, apr_size_t len)
+{
+ GlobalObject* ref = static_cast<GlobalObject*>(baton);
+ InputStream stream(Env(), ref->get());
+ return stream_skip(&stream, len);
+}
+
+svn_error_t*
+global_stream_close_output(void* baton)
+{
+ GlobalObject* ref = static_cast<GlobalObject*>(baton);
+ OutputStream stream(Env(), ref->get());
+ return stream_close_output(&stream);
+}
+
+svn_error_t*
+global_stream_write(void* baton, const char* data, apr_size_t* len)
+{
+ GlobalObject* ref = static_cast<GlobalObject*>(baton);
+ OutputStream stream(Env(), ref->get());
+ return stream_write(&stream, data, len);
+}
+
+apr_status_t cleanup_global_object(void* baton)
+{
+ delete static_cast<GlobalObject*>(baton);
+ return APR_SUCCESS;
+}
+} // anonymous namespace
+
+
+// Class Java::InputStream
+
+const char* const InputStream::m_class_name = "java/io/InputStream";
+InputStream::ClassImpl::ClassImpl(Env env, jclass cls)
+ : Object::ClassImpl(env, cls),
+ m_mid_close(env.GetMethodID(cls, "close", "()V")),
+ m_mid_mark_supported(env.GetMethodID(cls, "markSupported", "()Z")),
+ m_mid_mark(env.GetMethodID(cls, "mark", "(I)V")),
+ m_mid_reset(env.GetMethodID(cls, "reset", "()V")),
+ m_mid_read_byte(env.GetMethodID(cls, "read", "()I")),
+ m_mid_read_bytearray(env.GetMethodID(cls, "read", "([BII)I")),
+ m_mid_skip(env.GetMethodID(cls, "skip", "(J)J"))
+{}
+
+InputStream::ClassImpl::~ClassImpl() {}
+
+svn_stream_t*
+InputStream::get_global_stream(Env env, jobject jstream,
+ const SVN::Pool& pool)
+{
+ if (!jstream)
+ return NULL;
+
+ const bool has_mark = InputStream(env, jstream).mark_supported();
+
+ std::auto_ptr<GlobalObject> baton(new GlobalObject(env, jstream));
+
+ svn_stream_t* const stream = svn_stream_create(baton.get(), pool.getPool());
+ svn_stream_set_read2(stream, global_stream_read,
+ NULL /* only partial read support */);
+ svn_stream_set_skip(stream, global_stream_skip);
+ svn_stream_set_close(stream, global_stream_close_input);
+ if (has_mark)
+ {
+ svn_stream_set_mark(stream, global_stream_mark);
+ svn_stream_set_seek(stream, global_stream_seek);
+ }
+
+ apr_pool_cleanup_register(pool.getPool(), baton.release(),
+ cleanup_global_object,
+ apr_pool_cleanup_null);
+ return stream;
+}
+
+svn_stream_t* InputStream::get_stream(const SVN::Pool& pool)
+{
+ if (!m_jthis)
+ return NULL;
+
+ const bool has_mark = mark_supported();
+
+ svn_stream_t* const stream = svn_stream_create(this, pool.getPool());
+ svn_stream_set_read2(stream, stream_read,
+ NULL /* only partial read support */);
+ svn_stream_set_skip(stream, stream_skip);
+ svn_stream_set_close(stream, stream_close_input);
+ if (has_mark)
+ {
+ svn_stream_set_mark(stream, stream_mark);
+ svn_stream_set_seek(stream, stream_seek);
+ }
+ return stream;
+}
+
+jint InputStream::read(void* data, jint length)
+{
+ ByteArray array(m_env, length);
+ const jint size = read(array);
+ if (size > 0)
+ {
+ ByteArray::Contents contents(array);
+ ::memcpy(static_cast<char*>(data), contents.data(), size);
+ }
+ return size;
+}
+
+// Class Java::OutputStream
+
+const char* const OutputStream::m_class_name = "java/io/OutputStream";
+
+OutputStream::ClassImpl::ClassImpl(Env env, jclass cls)
+ : Object::ClassImpl(env, cls),
+ m_mid_close(env.GetMethodID(cls, "close", "()V")),
+ m_mid_write_byte(env.GetMethodID(cls, "write", "(I)V")),
+ m_mid_write_bytearray(env.GetMethodID(cls, "write", "([BII)V"))
+{}
+
+OutputStream::ClassImpl::~ClassImpl() {}
+
+svn_stream_t*
+OutputStream::get_global_stream(Env env, jobject jstream,
+ const SVN::Pool& pool)
+{
+ if (!jstream)
+ return NULL;
+
+ std::auto_ptr<GlobalObject> baton(new GlobalObject(env, jstream));
+
+ svn_stream_t* const stream = svn_stream_create(baton.get(), pool.getPool());
+ svn_stream_set_write(stream, global_stream_write);
+ svn_stream_set_close(stream, global_stream_close_output);
+
+ apr_pool_cleanup_register(pool.getPool(), baton.release(),
+ cleanup_global_object,
+ apr_pool_cleanup_null);
+ return stream;
+}
+
+svn_stream_t* OutputStream::get_stream(const SVN::Pool& pool)
+{
+ if (!m_jthis)
+ return NULL;
+
+ svn_stream_t* const stream = svn_stream_create(this, pool.getPool());
+ svn_stream_set_write(stream, stream_write);
+ svn_stream_set_close(stream, stream_close_output);
+ return stream;
+}
+
+} // namespace Java
diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_io_stream.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_io_stream.hpp
new file mode 100644
index 0000000..c0ee022
--- /dev/null
+++ b/subversion/bindings/javahl/native/jniwrapper/jni_io_stream.hpp
@@ -0,0 +1,281 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#ifndef SVN_JAVAHL_JNIWRAPPER_IO_STREAM_HPP
+#define SVN_JAVAHL_JNIWRAPPER_IO_STREAM_HPP
+
+#include <cstring>
+#include <string>
+#include <memory>
+
+#include "jni_object.hpp"
+#include "jni_array.hpp"
+
+#include "../Pool.h"
+
+#include "svn_io.h"
+
+namespace Java {
+
+/**
+ * Object wrapper for @c java.io.InputStream.
+ *
+ * @since New in 1.9.
+ */
+class InputStream : public Object
+{
+public:
+ /**
+ * Constructs a wrapper around an existing @c InputStream @a jstream.
+ */
+ explicit InputStream(Env env, jobject jstream)
+ : Object(env, ClassCache::get_input_stream(env), jstream)
+ {}
+
+ /**
+ * Creates a stand-alone @c svn_stream_t allocated from @a pool that
+ * contains a global reference to @a jstream. This stream can safely
+ * be used in long-lived bound objects.
+ */
+ static svn_stream_t*
+ get_global_stream(Env env, jobject jstream, const SVN::Pool& pool);
+
+ /**
+ * Creates an @c svn_stream_t allocated from @a pool.
+ * <b>Do not use the returned stream past the lifetime of the
+ * current JNI native frame.</b>
+ */
+ svn_stream_t* get_stream(const SVN::Pool& pool);
+
+public:
+ /**
+ * Implements @c InputStream.close()
+ */
+ void close()
+ {
+ m_env.CallVoidMethod(m_jthis, impl().m_mid_close);
+ }
+
+ /**
+ * Implements @c InputStream.markSupported()
+ */
+ bool mark_supported()
+ {
+ return m_env.CallBooleanMethod(m_jthis, impl().m_mid_mark_supported);
+ }
+
+ /**
+ * Implements @c InputStream.mark(int)
+ */
+ void mark(jint readlimit)
+ {
+ m_env.CallVoidMethod(m_jthis, impl().m_mid_mark, readlimit);
+ }
+
+ /**
+ * Implements @c InputStream.reset()
+ */
+ void reset()
+ {
+ m_env.CallVoidMethod(m_jthis, impl().m_mid_reset);
+ }
+
+ /**
+ * Implements @c InputStream.read()
+ */
+ jint read()
+ {
+ return m_env.CallIntMethod(m_jthis, impl().m_mid_read_byte);
+ }
+
+ /**
+ * Implements @c InputStream.read(byte[],int,int)
+ */
+ jint read(ByteArray& dst, jint length = -1, jint offset = 0)
+ {
+ return m_env.CallIntMethod(m_jthis, impl().m_mid_read_bytearray,
+ dst.get(), offset,
+ (length >= 0 ? length
+ : dst.length() - offset));
+ }
+
+ /**
+ * Helper method to read data into a native buffer.
+ */
+ jint read(void* data, jint length);
+
+ /**
+ * Implements @c InputStream.skip(long)
+ */
+ jlong skip(jlong count)
+ {
+ return m_env.CallLongMethod(m_jthis, impl().m_mid_skip, count);
+ }
+
+private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const MethodID m_mid_close;
+ const MethodID m_mid_mark_supported;
+ const MethodID m_mid_mark;
+ const MethodID m_mid_reset;
+ const MethodID m_mid_read_byte;
+ const MethodID m_mid_read_bytearray;
+ const MethodID m_mid_skip;
+ };
+
+ friend class ClassCacheImpl;
+ static const char* const m_class_name;
+
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+};
+
+
+/**
+ * Object wrapper for @c java.io.OutputStream.
+ *
+ * @since New in 1.9.
+ */
+class OutputStream : public Object
+{
+public:
+ /**
+ * Constructs a wrapper around an existing @c OutputStream @a jstream.
+ */
+ explicit OutputStream(Env env, jobject jstream)
+ : Object(env, ClassCache::get_output_stream(env), jstream)
+ {}
+
+ /**
+ * Creates a stand-alone @c svn_stream_t allocated from @a pool that
+ * contains a global reference to @a jstream. This stream can safely
+ * be used in long-lived bound objects.
+ */
+ static svn_stream_t*
+ get_global_stream(Env env, jobject jstream, const SVN::Pool& pool);
+
+ /**
+ * Creates an @c svn_stream_t allocated from @a pool.
+ * <b>Do not use the returned stream past the lifetime of the
+ * current JNI native frame.</b>
+ */
+ svn_stream_t* get_stream(const SVN::Pool& pool);
+
+public:
+ /**
+ * Implements @c OuptutStream.close()
+ */
+ void close()
+ {
+ m_env.CallVoidMethod(m_jthis, impl().m_mid_close);
+ }
+
+ /**
+ * Implements @c OuptutStream.write(int)
+ */
+ void write(jint byte)
+ {
+ m_env.CallVoidMethod(m_jthis, impl().m_mid_write_byte, byte);
+ }
+
+ /**
+ * Implements @c OuptutStream.write(byte[],int,int)
+ */
+ void write(const ByteArray& src, jint length = -1, jint offset = 0)
+ {
+ m_env.CallVoidMethod(m_jthis, impl().m_mid_write_bytearray,
+ src.get(), offset,
+ (length >= 0 ? length
+ : src.length() - offset));
+ }
+
+ /**
+ * Helper method to write data from a native buffer.
+ */
+ void write(const void* data, jint length, jint offset = 0)
+ {
+ write(ByteArray(m_env, data, length));
+ }
+
+ /**
+ * Helper method to write a C string to the stream.
+ */
+ void write(const char* text)
+ {
+ write(ByteArray(m_env, text));
+ }
+
+ /**
+ * Helper method to write a C++ string to the stream.
+ */
+ void write(const std::string& text)
+ {
+ write(ByteArray(m_env, text));
+ }
+
+private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const MethodID m_mid_close;
+ const MethodID m_mid_write_byte;
+ const MethodID m_mid_write_bytearray;
+ };
+
+ friend class ClassCacheImpl;
+ static void static_init(Env env);
+ static const char* const m_class_name;
+
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+};
+
+} // namespace Java
+
+#endif // SVN_JAVAHL_JNIWRAPPER_IO_STREAM_HPP
diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_iterator.cpp b/subversion/bindings/javahl/native/jniwrapper/jni_iterator.cpp
new file mode 100644
index 0000000..a6e3d1c
--- /dev/null
+++ b/subversion/bindings/javahl/native/jniwrapper/jni_iterator.cpp
@@ -0,0 +1,63 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#include <stdexcept>
+
+#include "jni_iterator.hpp"
+
+#include "svn_private_config.h"
+
+namespace Java {
+
+// Class Java::BaseIterator
+
+const char* const BaseIterator::m_class_name = "java/util/Iterator";
+
+BaseIterator::ClassImpl::ClassImpl(Env env, jclass cls)
+ : Object::ClassImpl(env, cls),
+ m_mid_has_next(env.GetMethodID(cls, "hasNext", "()Z")),
+ m_mid_next(env.GetMethodID(cls, "next", "()Ljava/lang/Object;"))
+{}
+
+BaseIterator::ClassImpl::~ClassImpl() {}
+
+jobject BaseIterator::next()
+{
+ try
+ {
+ return m_env.CallObjectMethod(m_jthis, impl().m_mid_next);
+ }
+ catch (const SignalExceptionThrown&)
+ {
+ // Just rethrow if it's not a NoSuchElementException.
+ if (!m_env.IsInstanceOf(
+ m_env.ExceptionOccurred(),
+ ClassCache::get_exc_no_such_element(m_env)->get_class()))
+ throw;
+
+ m_env.ExceptionClear();
+ throw std::range_error(_("Iterator out of bounds"));
+ }
+}
+
+} // namespace Java
diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_iterator.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_iterator.hpp
new file mode 100644
index 0000000..19c7976
--- /dev/null
+++ b/subversion/bindings/javahl/native/jniwrapper/jni_iterator.hpp
@@ -0,0 +1,91 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#ifndef SVN_JAVAHL_JNIWRAPPER_ITERATOR_HPP
+#define SVN_JAVAHL_JNIWRAPPER_ITERATOR_HPP
+
+#include "jni_env.hpp"
+#include "jni_object.hpp"
+
+namespace Java {
+
+/**
+ * Non-template base for a Java iterator.
+ *
+ * @since New in 1.9.
+ */
+class BaseIterator : public Object
+{
+protected:
+ /**
+ * Constructs the iterator wrapper.
+ */
+ explicit BaseIterator(Env env, jobject jiterator)
+ : Object(env, ClassCache::get_iterator(env), jiterator)
+ {}
+
+public:
+ /**
+ * Returns @c false at the end of the iteration.
+ */
+ bool has_next() const
+ {
+ return m_env.CallBooleanMethod(m_jthis, impl().m_mid_has_next);
+ }
+
+ /**
+ * Returns the next object in the iteration.
+ * @throw std::range_error if the next object is not available.
+ */
+ jobject next();
+
+private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const MethodID m_mid_has_next;
+ const MethodID m_mid_next;
+ };
+
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+
+ friend class ClassCacheImpl;
+ static const char* const m_class_name;
+};
+
+} // namespace Java
+
+#endif // SVN_JAVAHL_JNIWRAPPER_ITERATOR_HPP
diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_list.cpp b/subversion/bindings/javahl/native/jniwrapper/jni_list.cpp
new file mode 100644
index 0000000..e7b8a41
--- /dev/null
+++ b/subversion/bindings/javahl/native/jniwrapper/jni_list.cpp
@@ -0,0 +1,85 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#include <stdexcept>
+#include <string>
+
+#include "jni_list.hpp"
+
+#include "svn_private_config.h"
+
+namespace Java {
+
+// Class Java::BaseImmutableList
+
+const char* const BaseImmutableList::m_class_name = "java/util/List";
+
+BaseImmutableList::ClassImpl::ClassImpl(Env env, jclass cls)
+ : Object::ClassImpl(env, cls),
+ m_mid_size(env.GetMethodID(cls, "size", "()I")),
+ m_mid_get(env.GetMethodID(cls, "get", "(I)Ljava/lang/Object;")),
+ m_mid_add(env.GetMethodID(cls, "add", "(Ljava/lang/Object;)Z")),
+ m_mid_clear(env.GetMethodID(cls, "clear", "()V")),
+ m_mid_iter(env.GetMethodID(cls, "listIterator", "()Ljava/util/ListIterator;"))
+{}
+
+BaseImmutableList::ClassImpl::~ClassImpl() {}
+
+jobject BaseImmutableList::operator[](jint index) const
+{
+ try
+ {
+ return m_env.CallObjectMethod(m_jthis, impl().m_mid_get, index);
+ }
+ catch (const SignalExceptionThrown&)
+ {
+ // Just rethrow if it's not an IndexOutOfBoundsException.
+ if (!m_env.IsInstanceOf(
+ m_env.ExceptionOccurred(),
+ ClassCache::get_exc_index_out_of_bounds(m_env)->get_class()))
+ throw;
+
+ m_env.ExceptionClear();
+ std::string msg(_("List index out of bounds: "));
+ msg += index;
+ throw std::out_of_range(msg.c_str());
+ }
+}
+
+BaseImmutableList::Iterator BaseImmutableList::get_iterator() const
+{
+ return Iterator(m_env, m_env.CallObjectMethod(m_jthis, impl().m_mid_iter));
+}
+
+// Class Java::BaseList
+
+const char* const BaseList::m_class_name = "java/util/ArrayList";
+
+BaseList::ClassImpl::ClassImpl(Env env, jclass cls)
+ : BaseImmutableList::ClassImpl(env, cls),
+ m_mid_ctor(env.GetMethodID(cls, "<init>", "(I)V"))
+{}
+
+BaseList::ClassImpl::~ClassImpl() {}
+
+} // namespace Java
diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_list.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_list.hpp
new file mode 100644
index 0000000..9a11fc1
--- /dev/null
+++ b/subversion/bindings/javahl/native/jniwrapper/jni_list.hpp
@@ -0,0 +1,307 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#ifndef SVN_JAVAHL_JNIWRAPPER_LIST_HPP
+#define SVN_JAVAHL_JNIWRAPPER_LIST_HPP
+
+#include "jni_env.hpp"
+#include "jni_object.hpp"
+#include "jni_iterator.hpp"
+
+namespace Java {
+
+/**
+ * Non-template base for an immutable type-safe Java list.
+ *
+ * @since New in 1.9.
+ */
+class BaseImmutableList : public Object
+{
+public:
+ /**
+ * Returns the number of elements in the list.
+ */
+ jint length() const
+ {
+ return m_env.CallIntMethod(m_jthis, impl().m_mid_size);
+ }
+
+ /**
+ * Checks if the list is empty.
+ */
+ bool is_empty() const
+ {
+ return (length() == 0);
+ }
+
+protected:
+ /**
+ * Constructs the list wrapper
+ */
+ explicit BaseImmutableList(Env env, jobject jlist)
+ : Object(env, ClassCache::get_list(env), jlist)
+ {}
+
+ /**
+ * Constructor used by BaseList
+ */
+ explicit BaseImmutableList(Env env, const Object::ClassImpl* pimpl)
+ : Object(env, pimpl)
+ {}
+
+ /**
+ * Clears the contents of the list.
+ */
+ void clear()
+ {
+ m_env.CallVoidMethod(m_jthis, impl().m_mid_clear);
+ }
+
+ /**
+ * Appends @a obj to the end of the list.
+ */
+ void add(jobject obj)
+ {
+ m_env.CallVoidMethod(m_jthis, impl().m_mid_add, obj);
+ }
+
+ /**
+ * Returns the object reference at @a index.
+ * @throw std::out_of_range if the index value is not valid.
+ */
+ jobject operator[](jint index) const;
+
+ /**
+ * Iterator used by subclasses.
+ */
+ class Iterator : public BaseIterator
+ {
+ friend class BaseImmutableList;
+ explicit Iterator(Env env, jobject jiterator)
+ : BaseIterator(env, jiterator)
+ {}
+ };
+
+ /**
+ * Returns an iterator instance.
+ */
+ Iterator get_iterator() const;
+
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const MethodID m_mid_size;
+ const MethodID m_mid_get;
+ const MethodID m_mid_add;
+ const MethodID m_mid_clear;
+ const MethodID m_mid_iter;
+ };
+
+private:
+ friend class Iterator;
+ friend class ClassCacheImpl;
+ static const char* const m_class_name;
+
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+};
+
+/**
+ * Template wrapper for an immutable type-safe Java list.
+ *
+ * @since New in 1.9.
+ */
+template <typename T, typename NativeT=jobject>
+class ImmutableList : public BaseImmutableList
+{
+public:
+ /**
+ * Constructs the list wrapper.
+ */
+ explicit ImmutableList(Env env, jobject jlist)
+ : BaseImmutableList(env, jlist)
+ {}
+
+ /**
+ * 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, NativeT(BaseImmutableList::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
+ {
+ Iterator iter(get_iterator());
+ while (iter.has_next())
+ function(T(m_env, NativeT(iter.next())));
+ return function;
+ }
+};
+
+/**
+ * Non-template base for a mutable type-safe Java list.
+ *
+ * @since New in 1.9.
+ */
+class BaseList : public BaseImmutableList
+{
+public:
+ /**
+ * Clears the contents of the list.
+ */
+ void clear()
+ {
+ BaseImmutableList::clear();
+ }
+
+protected:
+ /**
+ * Constructs the list wrapper, treating @a jlist as a @c java.util.List.
+ */
+ explicit BaseList(Env env, jobject jlist)
+ : BaseImmutableList(env, jlist)
+ {}
+
+ /**
+ * Constructs and wraps an empty list of type @c java.util.ArrayList
+ * with initial allocation size @a length.
+ */
+ explicit BaseList(Env env, jint length)
+ : BaseImmutableList(env, ClassCache::get_array_list(env))
+ {
+ set_this(env.NewObject(get_class(), impl().m_mid_ctor, length));
+ }
+
+ /**
+ * Appends @a obj to the end of the list.
+ */
+ void add(jobject obj)
+ {
+ BaseImmutableList::add(obj);
+ }
+
+private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public BaseImmutableList::ClassImpl
+ {
+ friend class ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const MethodID m_mid_ctor;
+ };
+
+ friend class ClassCacheImpl;
+ static const char* const m_class_name;
+
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+};
+
+/**
+ * Template wrapper for a mutable type-safe Java list.
+ *
+ * @since New in 1.9.
+ */
+template <typename T, typename NativeT=jobject>
+class List : public BaseList
+{
+public:
+ /**
+ * Constructs the list wrapper, deriving the class from @a jlist.
+ */
+ explicit List(Env env, jobject jlist)
+ : BaseList(env, jlist)
+ {}
+
+ /**
+ * Constructs and wraps an empty list of type @c java.util.ArrayList
+ * with initial allocation size @a length.
+ */
+ explicit List(Env env, jint length_ = 0)
+ : BaseList(env, length_)
+ {}
+
+ /**
+ * 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, NativeT(BaseList::operator[](index)));
+ }
+
+ /**
+ * Appends @a obj to the end of the list.
+ */
+ void add(const T& obj)
+ {
+ BaseList::add(obj.get());
+ }
+
+ /**
+ * 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
+ {
+ Iterator iter(get_iterator());
+ while (iter.has_next())
+ function(T(m_env, NativeT(iter.next())));
+ return function;
+ }
+};
+
+} // namespace Java
+
+#endif // SVN_JAVAHL_JNIWRAPPER_LIST_HPP
diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_object.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_object.hpp
new file mode 100644
index 0000000..2e05a06
--- /dev/null
+++ b/subversion/bindings/javahl/native/jniwrapper/jni_object.hpp
@@ -0,0 +1,285 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#ifndef SVN_JAVAHL_JNIWRAPPER_OBJECT_HPP
+#define SVN_JAVAHL_JNIWRAPPER_OBJECT_HPP
+
+#include "jni_env.hpp"
+#include "jni_globalref.hpp"
+
+namespace Java {
+
+/**
+ * An abstract wrapper for a @c java.lang.Object instance.
+ *
+ * This is the base class for all concrete object wrapper classes. It
+ * is self-contained in the sense that it heeps its own JVM
+ * environment, class and object reference; Java object methods are
+ * expected to be exposed as methods of derived classes.
+ *
+ * The associated JNI class reference is stored for the lifetime of
+ * the JVM in the global class cache.
+ *
+ * @since New in 1.9.
+ */
+class Object
+{
+public:
+ /**
+ * Returns the wrapped JNI object reference.
+ */
+ jobject get() const
+ {
+ return m_jthis;
+ }
+
+ /**
+ * Returns the wrapped JNI class reference.
+ */
+ jclass get_class() const
+ {
+ return m_impl->get_class();
+ }
+
+ /**
+ * Returns the wrapped enviromnment reference.
+ */
+ Env get_env() const
+ {
+ return m_env;
+ }
+
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl
+ {
+ public:
+ jclass get_class() const
+ {
+ return m_class.get();
+ }
+
+ virtual ~ClassImpl();
+
+ protected:
+ explicit ClassImpl(Env env, jclass cls)
+ : m_class(env, cls)
+ {}
+
+ private:
+ friend class ClassCacheImpl;
+
+ GlobalClass m_class; ///< Class reference for this object wrapper
+
+ // Non-copyable
+ ClassImpl(const ClassImpl&);
+ ClassImpl& operator=(const ClassImpl&);
+ };
+
+protected:
+ /**
+ * constructs an object wrapper given the class @a impl and an
+ * object reference @a jthis.
+ */
+ Object(Env env, const ClassImpl* impl, jobject jthis = NULL)
+ : m_env(env),
+ m_impl(impl),
+ m_jthis(jthis)
+ {}
+
+ const Env m_env; ///< JVM environment wrapper
+ const ClassImpl* const m_impl; ///< Class implementation details
+ const jobject m_jthis; ///< @c this object reference
+
+ /**
+ * Certain subclasses need a fully constructed base Object before
+ * they can create the wrapped JNI object. They can use this
+ * function to oveerride the constness of @c m_jthis, but only if
+ * they're changing a @c NULL @c m_jthis to a concrete value.
+ */
+ void set_this(jobject jthis)
+ {
+ if (!m_jthis && jthis)
+ *const_cast<jobject*>(&m_jthis) = jthis;
+ }
+
+private:
+ friend class ClassCacheImpl;
+ static const char* const m_class_name;
+};
+
+// Forward declaration
+class ClassCacheImpl;
+
+/**
+ * A singleton cache for global class references.
+ *
+ * The instance is created when the native library is loaded by the
+ * JVM, and destroyed when it is unloaded. It creates global
+ * references for a number of classes and calls said classes'
+ * single-threded static initializers, which usually find and store
+ * method and field IDs (which are usually only valid until the
+ * associated class is garbage-collected).
+ *
+ * Be aware that as long as the global references exist, these classes
+ * cannot be garbage-collected. The number of classes stored in this
+ * cache should therefore be kept to a reasonable minimum.
+ *
+ * @since New in 1.9.
+ */
+class ClassCache
+{
+ // Cannot create instances of this type.
+ ClassCache();
+ ~ClassCache();
+
+ static ClassCacheImpl* m_impl;
+
+public:
+ /* This static initializer must only be called by JNI_OnLoad */
+ static void create();
+
+ /* This static finalizer must only be called by JNI_OnUnload */
+ static void destroy();
+
+#define JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(M) \
+ static const Object::ClassImpl* get_##M(Env env);
+
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(object);
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(classtype);
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(throwable);
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(string);
+
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(exc_index_out_of_bounds);
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(exc_no_such_element);
+
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(list);
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(array_list);
+
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(map);
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(set);
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(iterator);
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(map_entry);
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(hash_map);
+
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(input_stream);
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(output_stream);
+
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(byte_buffer);
+
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(subversion_exception);
+
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(authn_cb);
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(authn_result);
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(authn_ssl_server_cert_failures);
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(authn_ssl_server_cert_info);
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(user_passwd_cb);
+
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(credential);
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(credential_kind);
+
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(external_item);
+
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(editor_provide_base_cb);
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(editor_provide_base_cb_ret);
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(editor_provide_props_cb);
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(editor_provide_props_cb_ret);
+ JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(editor_get_kind_cb);
+#undef JNIWRAPPER_DECLARE_CACHED_CLASS
+};
+
+
+/**
+ * Object wrapper for @c java.lang.Class.
+ *
+ * The associated JNI class reference is stored for the lifetime of
+ * the JVM in the global class cache.
+ *
+ * @since New in 1.9.
+ */
+class Class
+{
+public:
+ /**
+ * Constructs class instance wrapper for @a obj.
+ */
+ explicit Class(Env env, jobject obj);
+
+ /**
+ * Constructs class instance wrapper for @a obj.
+ */
+ explicit Class(const Object& obj);
+
+ /**
+ * Wrapper for the Java @c getName() method.
+ */
+ jstring get_name() const;
+
+ /**
+ * Returns the wrapped class instance.
+ */
+ jobject get() const
+ {
+ return m_jthis;
+ }
+
+ /**
+ * Returns the wrapped enviromnment reference.
+ */
+ Env get_env() const
+ {
+ return m_env;
+ }
+
+private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(Env env, jclass cls)
+ : Object::ClassImpl(env, cls)
+ {}
+
+ public:
+ virtual ~ClassImpl();
+ };
+
+ const Env m_env; ///< JVM environment wrapper
+ const jobject m_jthis; ///< Class instance
+
+ friend class ClassCacheImpl;
+ static const char* const m_class_name;
+ static void static_init(Env env, jclass class_type);
+
+ static MethodID m_mid_get_class;
+ static MethodID m_mid_get_name;
+};
+
+} // namespace Java
+
+#endif // SVN_JAVAHL_JNIWRAPPER_OBJECT_HPP
diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp
new file mode 100644
index 0000000..13e35e3
--- /dev/null
+++ b/subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp
@@ -0,0 +1,220 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#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.
+ *
+ * Every native method implementation should start by invoking this
+ * macro to initialize the logging stack element and begin the
+ * 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.
+ *
+ * This macro expects two additional parameters to be available
+ * (either as function arguments or local variables):
+ * @c JNIEnv* @a jenv and @c jobject @a jthis.
+ *
+ * @since New in 1.9.
+ */
+#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
+ * argument is not available.
+ *
+ * This macro expects two additional parameters to be available
+ * (either as function arguments or local variables):
+ * @c JNIEnv* @a jenv and @c jclass @a jclazz.
+ *
+ * @since New in 1.9.
+ */
+#define SVN_JAVAHL_JNI_TRY_STATIC(C, M) \
+ ::JNIStackElement st_ac_ke_le_me_nt_(jenv, #C, #M, jclazz); \
+ try
+
+
+/**
+ * Boilerplate for the native method implementation exit point.
+ *
+ * Every native method implementation should end by invoking this
+ * 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_TO_EXCEPTION(X) \
+ catch (const ::Java::SignalExceptionThrown&) \
+ { \
+ SVN_JAVAHL_ASSERT_EXCEPTION_THROWN(::Java::Env(jenv)); \
+ } \
+ catch (const ::std::exception& ex) \
+ { \
+ X(::Java::Env(jenv)).throw_java_exception(ex.what()); \
+ } \
+ catch (...) \
+ { \
+ const char* const msg = Java::unknown_cxx_exception_message(); \
+ X(::Java::Env(jenv)).throw_java_exception(msg); \
+ }
+
+/**
+ * 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, 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 Java::caught_java_exception_error((C)); \
+ } \
+ 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 = Java::unknown_cxx_exception_message(); \
+ ::Java::RuntimeException((E)).throw_java_exception(msg); \
+ return svn_error_create((C), NULL, msg); \
+ }
+
+/**
+ * Exception checker for the oldstyle implementation that does not use
+ * the @c Java::Env environment wrapper.
+ *
+ * @param E A wrapped environment (@c Java::Env) instance.
+ *
+ * @since New in 1.9.
+ */
+#define SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(E) \
+ do { \
+ if ((E).ExceptionCheck()) \
+ throw ::Java::SignalExceptionThrown(); \
+ } while(0)
+
+
+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);
+
+/**
+ * Return a localized error string for an unknown C++ exception.
+ *
+ * @since New in 1.9.
+ */
+const char* unknown_cxx_exception_message() throw();
+
+/**
+ * Create an svn_error_t for a caught Java exception.
+ *
+ * @since New in 1.9.
+ */
+svn_error_t* caught_java_exception_error(apr_status_t status) throw();
+
+} // namespace Java
+
+#endif // SVN_JAVAHL_JNIWRAPPER_STACK_HPP
diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_string.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_string.hpp
new file mode 100644
index 0000000..7b874d0
--- /dev/null
+++ b/subversion/bindings/javahl/native/jniwrapper/jni_string.hpp
@@ -0,0 +1,251 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#ifndef SVN_JAVAHL_JNIWRAPPER_STRING_HPP
+#define SVN_JAVAHL_JNIWRAPPER_STRING_HPP
+
+#include <cstring>
+#include <string>
+
+#include <apr_pools.h>
+
+#include "jni_object.hpp"
+
+namespace Java {
+
+/**
+ * Object wrapper for @c java.lang.String.
+ *
+ * The associated JNI class reference is stored for the lifetime of
+ * the JVM in the global class cache.
+ *
+ * @since New in 1.9.
+ */
+class String
+{
+public:
+ /**
+ * Constructs a wrapper around an existing string @a str.
+ */
+ explicit String(Env env, jstring str)
+ : m_env(env),
+ m_jthis(str)
+ {}
+
+ /**
+ * Constructs a new string and wrapper from @a text.
+ */
+ explicit String(Env env, const char* text)
+ : m_env(env),
+ m_jthis(env.NewStringUTF(text))
+ {}
+
+ /**
+ * Constructs a new string and wrapper from @a text.
+ */
+ explicit String(Env env, const std::string& text)
+ : m_env(env),
+ m_jthis(env.NewStringUTF(text.c_str()))
+ {}
+
+ /**
+ * Returns the wrapped JNI object reference.
+ */
+ jstring get() const
+ {
+ return m_jthis;
+ }
+
+ /**
+ * Returns the wrapped enviromnment reference.
+ */
+ Env get_env() const
+ {
+ return m_env;
+ }
+
+ /**
+ * Returns the number of Unicode characters in the string.
+ */
+ jsize length() const
+ {
+ return m_env.GetStringLength(get());
+ }
+
+ /**
+ * Returns the length of the modified UTF-8 representation of the
+ * string.
+ */
+ jsize utf8_length() const
+ {
+ return m_env.GetStringUTFLength(get());
+ }
+
+ /**
+ * Copies the contents of the modified UTF-8 representation of the
+ * string into @a pool.
+ */
+ const char* strdup(apr_pool_t* pool) const;
+
+ /**
+ * Accessor class for the contents of the string.
+ *
+ * Objects of this class should be created within the scope where
+ * the raw C string is required. They will create an immutable
+ * modified UTF-8 representation of the string contents. The data
+ * will be released by the destructor.
+ */
+ class Contents
+ {
+ public:
+ /**
+ * Constructs an immutable string contents accessor.
+ */
+ explicit Contents(const String& str)
+ : m_str(str),
+ m_text(!str.get() ? NULL
+ : str.m_env.GetStringUTFChars(str.get(), NULL)),
+ m_length(m_text ? jsize(::std::strlen(m_text)) : 0)
+ {}
+
+ /**
+ * Releases the string contents, possibly committing changes to the JVM.
+ */
+ ~Contents()
+ {
+ if (m_text)
+ m_str.m_env.ReleaseStringUTFChars(m_str.get(), NULL);
+ }
+
+ /**
+ * Returns the C representation of the string contents.
+ */
+ const char* c_str() const
+ {
+ return m_text;
+ }
+
+ /**
+ * Returns the length of the C representation of the string.
+ */
+ jsize utf8_length() const
+ {
+ return m_length;
+ }
+
+ protected:
+ const String& m_str;
+ const char* m_text;
+ jsize m_length;
+ };
+
+ /**
+ * Accessor class for the contents of the string.
+ *
+ * Behaves like the #Contents class, but the representation is
+ * considered mutable and can be assigned a new value, which will be
+ * subsequently committed to the JVM.
+ */
+ class MutableContents : protected Contents
+ {
+ public:
+ /**
+ * Constructs a mutable string contents accessor.
+ */
+ explicit MutableContents(String& str)
+ : Contents(str),
+ m_new_text(NULL)
+ {}
+
+ /**
+ * Releases the string contents, possibly committing changes to the JVM.
+ */
+ ~MutableContents()
+ {
+ if (m_new_text)
+ {
+ // Prevent double-release by the Contents destructor.
+ m_text = NULL;
+ m_str.m_env.ReleaseStringUTFChars(m_str.get(), m_new_text);
+ }
+ }
+
+ /**
+ * Returns the C representation of the string contents.
+ */
+ const char* c_str() const
+ {
+ if (m_new_text)
+ return m_new_text;
+ return Contents::c_str();
+ }
+
+ /**
+ * Returns the length of the C representation of the string.
+ */
+ jsize utf8_length() const
+ {
+ return Contents::utf8_length();
+ }
+
+ /**
+ * Sets a new value for the string, to be committed to the JVM
+ * when the accessor object is destroyed.
+ * @throw std::invalid_argument if the @a new_text is @c null
+ * @throw std::logic_error if this is a @c null or immutable string
+ */
+ void set_value(const char* new_text);
+
+ private:
+ const char* m_new_text;
+ };
+
+private:
+ const Env m_env;
+ const jstring m_jthis;
+
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(Env env, jclass cls)
+ : Object::ClassImpl(env, cls)
+ {}
+
+ public:
+ virtual ~ClassImpl();
+ };
+
+ friend class Contents;
+ friend class MutableContents;
+ friend class ClassCacheImpl;
+ static const char* const m_class_name;
+};
+
+} // namespace Java
+
+#endif // SVN_JAVAHL_JNIWRAPPER_STRING_HPP
diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_string_map.cpp b/subversion/bindings/javahl/native/jniwrapper/jni_string_map.cpp
new file mode 100644
index 0000000..369d956
--- /dev/null
+++ b/subversion/bindings/javahl/native/jniwrapper/jni_string_map.cpp
@@ -0,0 +1,110 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#include <stdexcept>
+
+#include "jni_string.hpp"
+#include "jni_string_map.hpp"
+
+#include "svn_private_config.h"
+
+namespace Java {
+
+// Class Java::BaseImmutableMap
+
+const char* const BaseImmutableMap::m_class_name = "java/util/Map";
+
+BaseImmutableMap::ClassImpl::ClassImpl(Env env, jclass cls)
+ : Object::ClassImpl(env, cls),
+ m_mid_put(env.GetMethodID(cls, "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)"
+ "Ljava/lang/Object;")),
+ m_mid_clear(env.GetMethodID(cls, "clear", "()V")),
+ m_mid_has_key(env.GetMethodID(cls, "containsKey",
+ "(Ljava/lang/Object;)Z")),
+ m_mid_get(env.GetMethodID(cls, "get",
+ "(Ljava/lang/Object;)Ljava/lang/Object;")),
+ m_mid_size(env.GetMethodID(cls, "size", "()I")),
+ m_mid_entry_set(env.GetMethodID(cls, "entrySet", "()Ljava/util/Set;"))
+{}
+
+BaseImmutableMap::ClassImpl::~ClassImpl() {}
+
+jobject BaseImmutableMap::operator[](const std::string& index) const
+{
+ const String key(m_env, index);
+ if (!m_env.CallBooleanMethod(m_jthis, impl().m_mid_has_key, key.get()))
+ {
+ std::string msg(_("Map does not contain key: "));
+ msg += index;
+ throw std::out_of_range(msg.c_str());
+ }
+ return m_env.CallObjectMethod(m_jthis, impl().m_mid_get, key.get());
+}
+
+BaseImmutableMap::Iterator BaseImmutableMap::get_iterator() const
+{
+ const jobject jentry_set =
+ m_env.CallObjectMethod(m_jthis, impl().m_mid_entry_set);
+ const jobject jiterator =
+ m_env.CallObjectMethod(jentry_set, Set::impl(m_env).m_mid_iterator);
+ return Iterator(m_env, jiterator);
+}
+
+// Class Java::BaseImmutableMap::Entry
+
+const char* const BaseImmutableMap::Entry::m_class_name = "java/util/Map$Entry";
+
+BaseImmutableMap::Entry::ClassImpl::ClassImpl(Env env, jclass cls)
+ : Object::ClassImpl(env, cls),
+ m_mid_get_key(env.GetMethodID(cls, "getKey", "()Ljava/lang/Object;")),
+ m_mid_get_value(env.GetMethodID(cls, "getValue", "()Ljava/lang/Object;"))
+{}
+
+BaseImmutableMap::Entry::ClassImpl::~ClassImpl() {}
+
+// Class Java::BaseImmutableMap::Set
+
+const char* const BaseImmutableMap::Set::m_class_name = "java/util/Set";
+
+BaseImmutableMap::Set::ClassImpl::ClassImpl(Env env, jclass cls)
+ : Object::ClassImpl(env, cls),
+ m_mid_iterator(env.GetMethodID(cls, "iterator",
+ "()Ljava/util/Iterator;"))
+{}
+
+BaseImmutableMap::Set::ClassImpl::~ClassImpl() {}
+
+
+// Class Java::BaseMap
+
+const char* const BaseMap::m_class_name = "java/util/HashMap";
+
+BaseMap::ClassImpl::ClassImpl(Env env, jclass cls)
+ : BaseImmutableMap::ClassImpl(env, cls),
+ m_mid_ctor(env.GetMethodID(cls, "<init>", "(I)V"))
+{}
+
+BaseMap::ClassImpl::~ClassImpl() {}
+
+} // namespace Java
diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_string_map.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_string_map.hpp
new file mode 100644
index 0000000..db05e28
--- /dev/null
+++ b/subversion/bindings/javahl/native/jniwrapper/jni_string_map.hpp
@@ -0,0 +1,384 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#ifndef SVN_JAVAHL_JNIWRAPPER_STRING_MAP_HPP
+#define SVN_JAVAHL_JNIWRAPPER_STRING_MAP_HPP
+
+#include <string>
+
+#include "jni_env.hpp"
+#include "jni_object.hpp"
+#include "jni_iterator.hpp"
+
+namespace Java {
+
+/**
+ * Non-template base for an immutable type-safe Java map with String keys.
+ *
+ * @since New in 1.9.
+ */
+class BaseImmutableMap : public Object
+{
+public:
+ /**
+ * Returns the number of elements in the map.
+ */
+ jint length() const
+ {
+ return m_env.CallIntMethod(m_jthis, impl().m_mid_size);
+ }
+
+ /**
+ * Checks if the map is empty.
+ */
+ bool is_empty() const
+ {
+ return (length() == 0);
+ }
+
+protected:
+ /**
+ * Constructs the map wrapper.
+ */
+ explicit BaseImmutableMap(Env env, jobject jmap)
+ : Object(env, ClassCache::get_map(env), jmap)
+ {}
+
+ /**
+ * Constructor used by BaseMap.
+ */
+ explicit BaseImmutableMap(Env env, const Object::ClassImpl* pimpl)
+ : Object(env, pimpl)
+ {}
+
+ /**
+ * Clears the contents of the map.
+ */
+ void clear()
+ {
+ m_env.CallVoidMethod(m_jthis, impl().m_mid_clear);
+ }
+
+ /**
+ * Inserts @a obj identified by @a key into the map.
+ */
+ void put(const std::string& key, jobject obj)
+ {
+ m_env.CallObjectMethod(m_jthis, impl().m_mid_put,
+ String(m_env, key).get(), obj);
+ }
+
+ /**
+ * Returns the object reference identified by @a index.
+ * @throw std::out_of_range if there is no such element.
+ */
+ jobject operator[](const std::string& index) const;
+
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const MethodID m_mid_put;
+ const MethodID m_mid_clear;
+ const MethodID m_mid_has_key;
+ const MethodID m_mid_get;
+ const MethodID m_mid_size;
+ const MethodID m_mid_entry_set;
+ };
+
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+
+ friend class ClassCacheImpl;
+ static const char* const m_class_name;
+
+ class Iterator : public BaseIterator
+ {
+ friend class BaseImmutableMap;
+ explicit Iterator(Env env, jobject jiterator)
+ : BaseIterator(env, jiterator)
+ {}
+ };
+
+ Iterator get_iterator() const;
+
+ class Entry : public Object
+ {
+ public:
+ explicit Entry(Env env, jobject jentry)
+ : Object(env, ClassCache::get_map_entry(env), jentry)
+ {}
+
+ const std::string key() const
+ {
+ const jstring jkey =
+ jstring(m_env.CallObjectMethod(m_jthis, impl().m_mid_get_key));
+ const String::Contents key(String(m_env, jkey));
+ return std::string(key.c_str());
+ }
+
+ jobject value() const
+ {
+ return m_env.CallObjectMethod(m_jthis, impl().m_mid_get_value);
+ }
+
+ private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const MethodID m_mid_get_key;
+ const MethodID m_mid_get_value;
+ };
+
+ friend class ClassCacheImpl;
+ static const char* const m_class_name;
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+ };
+
+private:
+ struct Set
+ {
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public Object::ClassImpl
+ {
+ friend class ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const MethodID m_mid_iterator;
+ };
+
+ static const char* const m_class_name;
+ static const ClassImpl& impl(Env env)
+ {
+ return *dynamic_cast<const ClassImpl*>(ClassCache::get_set(env));
+ }
+ };
+};
+
+/**
+ * Template wrapper for an immutable type-safe Java map.
+ *
+ * @since New in 1.9.
+ */
+template <typename T, typename NativeT=jobject>
+class ImmutableMap : public BaseImmutableMap
+{
+public:
+ /**
+ * Constructs the map wrapper, converting the contents to an
+ * @c std::map.
+ */
+ explicit ImmutableMap(Env env, jobject jmap)
+ : BaseImmutableMap(env, jmap)
+ {}
+
+ /**
+ * Returns a wrapper object for the object reference identified by @a index.
+ * @throw std::out_of_range if there is no such element.
+ */
+ T operator[](const std::string& index) const
+ {
+ return T(m_env, NativeT(BaseImmutableMap::operator[](index)));
+ }
+
+ /**
+ * Iterates over the items in the map, calling @a function for
+ * each item.
+ * @see std::for_each
+ * @note Unlike std::for_each, which invokes the functor with a
+ * single @c value_type argument, this iterator calls
+ * @a function with separate @c const references to the key
+ * and value.
+ */
+ template<typename F>
+ F for_each(F function) const
+ {
+ Iterator iter(get_iterator());
+ while (iter.has_next())
+ {
+ Entry entry(m_env, iter.next());
+ const std::string& key(entry.key());
+ function(key, T(m_env, NativeT(entry.value())));
+ }
+ return function;
+ }
+};
+
+/**
+ * Non-template base for a mutable type-safe Java map with String keys.
+ *
+ * @since New in 1.9.
+ */
+class BaseMap : public BaseImmutableMap
+{
+public:
+ /**
+ * Clears the contents of the map.
+ */
+ void clear()
+ {
+ BaseImmutableMap::clear();
+ }
+
+protected:
+ /**
+ * Constructs the map wrapper, treating @a jmap as a @c java.util.Map.
+ */
+ explicit BaseMap(Env env, jobject jmap)
+ : BaseImmutableMap(env, jmap)
+ {}
+
+ /**
+ * Constructs and wraps an empty map of type @c java.util.HashMap
+ * with initial allocation size @a length.
+ */
+ explicit BaseMap(Env env, jint length)
+ : BaseImmutableMap(env, ClassCache::get_hash_map(env))
+ {
+ set_this(env.NewObject(get_class(), impl().m_mid_ctor, length));
+ }
+
+private:
+ /**
+ * This object's implementation details.
+ */
+ class ClassImpl : public BaseImmutableMap::ClassImpl
+ {
+ friend class ClassCacheImpl;
+
+ protected:
+ explicit ClassImpl(Env env, jclass cls);
+
+ public:
+ virtual ~ClassImpl();
+
+ const MethodID m_mid_ctor;
+ };
+
+ const ClassImpl& impl() const
+ {
+ return *dynamic_cast<const ClassImpl*>(m_impl);
+ }
+
+ friend class ClassCacheImpl;
+ static const char* const m_class_name;
+};
+
+/**
+ * Template wrapper for a mutable type-safe Java map.
+ *
+ * @since New in 1.9.
+ */
+template <typename T, typename NativeT=jobject>
+class Map : public BaseMap
+{
+public:
+ /**
+ * Constructs the map wrapper, deriving the class from @a jmap.
+ */
+ explicit Map(Env env, jobject jmap)
+ : BaseMap(env, jmap)
+ {}
+
+ /**
+ * Constructs and wraps an empty map of type @c java.util.HashMap
+ * with initial allocation size @a length.
+ */
+ explicit Map(Env env, jint length = 0)
+ : BaseMap(env, length)
+ {}
+
+ /**
+ * Inserts @a obj identified by @a key into the map.
+ */
+ void put(const std::string& key, const T& obj)
+ {
+ BaseMap::put(key, obj.get());
+ }
+
+ /**
+ * Returns a wrapper object for the object reference identified by @a index.
+ * @throw std::out_of_range if there is no such element.
+ */
+ T operator[](const std::string& index) const
+ {
+ return T(m_env, NativeT(BaseMap::operator[](index)));
+ }
+
+ /**
+ * Iterates over the items in the map, calling @a function for
+ * each item.
+ * @see std::for_each
+ * @note Unlike std::for_each, which invokes the functor with a
+ * single @c value_type argument, this iterator calls
+ * @a function with separate @c const references to the key
+ * and value.
+ */
+ template<typename F>
+ F for_each(F function) const
+ {
+ Iterator iter(get_iterator());
+ while (iter.has_next())
+ {
+ Entry entry(m_env, iter.next());
+ const std::string& key(entry.key());
+ function(key, T(m_env, NativeT(entry.value())));
+ }
+ return function;
+ }
+};
+
+} // namespace Java
+
+#endif // SVN_JAVAHL_JNIWRAPPER_STRING_MAP_HPP
diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp
index 9705304..a6be60f 100644
--- a/subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp
+++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp
@@ -25,14 +25,11 @@
* NativeResources.
*/
-#include "JNIUtil.h"
#include "../include/org_apache_subversion_javahl_NativeResources.h"
JNIEXPORT void JNICALL
Java_org_apache_subversion_javahl_NativeResources_initNativeLibrary
(JNIEnv *env, jclass jclazz)
{
- // No usual JNIEntry here, as the prerequisite native library
- // initialization is performed here.
- JNIUtil::JNIGlobalInit(env);
+ // No-op; see comment in NativeResources.java
}
diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNClient.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNClient.cpp
index e4fdd76..5f70a5a 100644
--- a/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNClient.cpp
+++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNClient.cpp
@@ -50,7 +50,8 @@
#include "ImportFilterCallback.h"
#include "ChangelistCallback.h"
#include "StringArray.h"
-#include "RevpropTable.h"
+#include "PropertyTable.h"
+#include "CreateJ.h"
#include "VersionExtended.h"
#include "DiffOptions.h"
#include "svn_version.h"
@@ -184,8 +185,9 @@ Java_org_apache_subversion_javahl_SVNClient_list
JNIEXPORT void JNICALL
Java_org_apache_subversion_javahl_SVNClient_status
(JNIEnv *env, jobject jthis, jstring jpath, jobject jdepth,
- jboolean jonServer, jboolean jgetAll, jboolean jnoIgnore,
- jboolean jignoreExternals, jobject jchangelists,
+ jboolean jonServer, jboolean jonDisk, jboolean jgetAll,
+ jboolean jnoIgnore, jboolean jignoreExternals,
+ jboolean jdepthAsSticky, jobject jchangelists,
jobject jstatusCallback)
{
JNIEntry(SVNClient, status);
@@ -203,9 +205,9 @@ Java_org_apache_subversion_javahl_SVNClient_status
StatusCallback callback(jstatusCallback);
cl->status(path, EnumMapper::toDepth(jdepth),
- jonServer ? true:false,
- jgetAll ? true:false, jnoIgnore ? true:false,
- jignoreExternals ? true:false, changelists, &callback);
+ bool(jonServer), bool(jonDisk), bool(jgetAll),
+ bool(jnoIgnore), bool(jignoreExternals),
+ bool(jdepthAsSticky), changelists, &callback);
}
JNIEXPORT void JNICALL
@@ -257,7 +259,7 @@ Java_org_apache_subversion_javahl_SVNClient_password
}
JNIEXPORT void JNICALL
-Java_org_apache_subversion_javahl_SVNClient_setPrompt
+Java_org_apache_subversion_javahl_SVNClient_setPrompt__Lorg_apache_subversion_javahl_callback_AuthnCallback_2
(JNIEnv *env, jobject jthis, jobject jprompter)
{
JNIEntry(SVNClient, setPrompt);
@@ -267,7 +269,7 @@ Java_org_apache_subversion_javahl_SVNClient_setPrompt
JNIUtil::throwError(_("bad C++ this"));
return;
}
- Prompter *prompter = Prompter::makeCPrompter(jprompter);
+ Prompter::UniquePtr prompter(Prompter::create(jprompter));
if (JNIUtil::isExceptionThrown())
return;
@@ -275,6 +277,39 @@ Java_org_apache_subversion_javahl_SVNClient_setPrompt
}
JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_SVNClient_setPrompt__Lorg_apache_subversion_javahl_callback_UserPasswordCallback_2
+(JNIEnv *env, jobject jthis, jobject jprompter)
+{
+ JNIEntry(SVNClient, setPrompt);
+ SVNClient *cl = SVNClient::getCppObject(jthis);
+ if (cl == NULL)
+ {
+ JNIUtil::throwError(_("bad C++ this"));
+ return;
+ }
+ Prompter::UniquePtr prompter(CompatPrompter::create(jprompter));
+ if (JNIUtil::isExceptionThrown())
+ return;
+
+ cl->getClientContext().setPrompt(prompter);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_SVNClient_setTunnelAgent
+(JNIEnv *env, jobject jthis, jobject jtunnelcb)
+{
+ JNIEntry(SVNClient, setPrompt);
+ SVNClient *cl = SVNClient::getCppObject(jthis);
+ if (cl == NULL)
+ {
+ JNIUtil::throwError(_("bad C++ this"));
+ return;
+ }
+
+ cl->getClientContext().setTunnelCallback(jtunnelcb);
+}
+
+JNIEXPORT void JNICALL
Java_org_apache_subversion_javahl_SVNClient_logMessages
(JNIEnv *env, jobject jthis, jstring jpath, jobject jpegRevision,
jobject jranges, jboolean jstopOnCopy, jboolean jdisoverPaths,
@@ -282,6 +317,14 @@ Java_org_apache_subversion_javahl_SVNClient_logMessages
jobject jlogMessageCallback)
{
JNIEntry(SVNClient, logMessages);
+
+ if (jlong(int(jlimit)) != jlimit)
+ {
+ JNIUtil::raiseThrowable("java/lang/IllegalArgumentException",
+ "The value of 'limit' is too large");
+ return;
+ }
+
SVNClient *cl = SVNClient::getCppObject(jthis);
if (cl == NULL)
{
@@ -323,7 +366,7 @@ Java_org_apache_subversion_javahl_SVNClient_logMessages
cl->logMessages(path, pegRevision, revisionRanges,
jstopOnCopy ? true: false, jdisoverPaths ? true : false,
jincludeMergedRevisions ? true : false,
- revProps, static_cast<long>(jlimit), &callback);
+ revProps, int(jlimit), &callback);
}
JNIEXPORT jlong JNICALL
@@ -384,7 +427,7 @@ Java_org_apache_subversion_javahl_SVNClient_remove
if (JNIUtil::isExceptionThrown())
return;
- RevpropTable revprops(jrevpropTable);
+ PropertyTable revprops(jrevpropTable, false, false);
if (JNIUtil::isExceptionThrown())
return;
@@ -395,8 +438,8 @@ Java_org_apache_subversion_javahl_SVNClient_remove
JNIEXPORT void JNICALL
Java_org_apache_subversion_javahl_SVNClient_revert
-(JNIEnv *env, jobject jthis, jstring jpath, jobject jdepth,
- jobject jchangelists)
+(JNIEnv *env, jobject jthis, jobject jpaths, jobject jdepth,
+ jobject jchangelists, jboolean jclear_changelists, jboolean jmetadata_only)
{
JNIEntry(SVNClient, revert);
SVNClient *cl = SVNClient::getCppObject(jthis);
@@ -405,7 +448,9 @@ Java_org_apache_subversion_javahl_SVNClient_revert
JNIUtil::throwError(_("bad C++ this"));
return;
}
- JNIStringHolder path(jpath);
+
+ SVN_JNI_NULL_PTR_EX(jpaths, "paths", );
+ StringArray paths(jpaths);
if (JNIUtil::isExceptionThrown())
return;
@@ -413,7 +458,8 @@ Java_org_apache_subversion_javahl_SVNClient_revert
if (JNIUtil::isExceptionThrown())
return;
- cl->revert(path, EnumMapper::toDepth(jdepth), changelists);
+ cl->revert(paths, EnumMapper::toDepth(jdepth),
+ changelists, bool(jclear_changelists), bool(jmetadata_only));
}
JNIEXPORT void JNICALL
@@ -498,7 +544,7 @@ Java_org_apache_subversion_javahl_SVNClient_commit
if (JNIUtil::isExceptionThrown())
return;
- RevpropTable revprops(jrevpropTable);
+ PropertyTable revprops(jrevpropTable, false, false);
if (JNIUtil::isExceptionThrown())
return;
@@ -513,6 +559,7 @@ JNIEXPORT void JNICALL
Java_org_apache_subversion_javahl_SVNClient_copy
(JNIEnv *env, jobject jthis, jobject jcopySources, jstring jdestPath,
jboolean jcopyAsChild, jboolean jmakeParents, jboolean jignoreExternals,
+ jboolean jmetadataOnly, jboolean jpinExternals, jobject jexternalsToPin,
jobject jrevpropTable, jobject jmessage, jobject jcallback)
{
JNIEntry(SVNClient, copy);
@@ -539,13 +586,15 @@ Java_org_apache_subversion_javahl_SVNClient_copy
if (JNIUtil::isExceptionThrown())
return;
- RevpropTable revprops(jrevpropTable);
+ PropertyTable revprops(jrevpropTable, false, false);
if (JNIUtil::isExceptionThrown())
return;
CommitCallback callback(jcallback);
- cl->copy(copySources, destPath, &message, jcopyAsChild ? true : false,
- jmakeParents ? true : false, jignoreExternals ? true : false,
+ cl->copy(copySources, destPath, &message,
+ bool(jcopyAsChild), bool(jmakeParents),
+ bool(jignoreExternals), bool(jmetadataOnly),
+ bool(jpinExternals), jexternalsToPin,
revprops, jcallback ? &callback : NULL);
}
@@ -577,7 +626,7 @@ Java_org_apache_subversion_javahl_SVNClient_move
if (JNIUtil::isExceptionThrown())
return;
- RevpropTable revprops(jrevpropTable);
+ PropertyTable revprops(jrevpropTable, false, false);
if (JNIUtil::isExceptionThrown())
return;
@@ -610,7 +659,7 @@ Java_org_apache_subversion_javahl_SVNClient_mkdir
if (JNIUtil::isExceptionThrown())
return;
- RevpropTable revprops(jrevpropTable);
+ PropertyTable revprops(jrevpropTable, false, false);
if (JNIUtil::isExceptionThrown())
return;
@@ -621,7 +670,10 @@ Java_org_apache_subversion_javahl_SVNClient_mkdir
JNIEXPORT void JNICALL
Java_org_apache_subversion_javahl_SVNClient_cleanup
-(JNIEnv *env, jobject jthis, jstring jpath)
+(JNIEnv *env, jobject jthis, jstring jpath,
+ jboolean jbreakLocks, jboolean jfixRecordedTimestamps,
+ jboolean jclearDavCache, jboolean jremoveUnusedPristines,
+ jboolean jincludeExternals)
{
JNIEntry(SVNClient, cleanup);
SVNClient *cl = SVNClient::getCppObject(jthis);
@@ -634,7 +686,9 @@ Java_org_apache_subversion_javahl_SVNClient_cleanup
if (JNIUtil::isExceptionThrown())
return;
- cl->cleanup(path);
+ cl->cleanup(path, jbreakLocks, jfixRecordedTimestamps,
+ jclearDavCache, jremoveUnusedPristines,
+ jincludeExternals);
}
JNIEXPORT void JNICALL
@@ -660,7 +714,8 @@ JNIEXPORT jlong JNICALL
Java_org_apache_subversion_javahl_SVNClient_doExport
(JNIEnv *env, jobject jthis, jstring jsrcPath, jstring jdestPath,
jobject jrevision, jobject jpegRevision, jboolean jforce,
- jboolean jignoreExternals, jobject jdepth, jstring jnativeEOL)
+ jboolean jignoreExternals, jboolean jignoreKeywords,
+ jobject jdepth, jstring jnativeEOL)
{
JNIEntry(SVNClient, doExport);
SVNClient *cl = SVNClient::getCppObject(jthis);
@@ -690,7 +745,9 @@ Java_org_apache_subversion_javahl_SVNClient_doExport
return -1;
return cl->doExport(srcPath, destPath, revision, pegRevision,
- jforce ? true : false, jignoreExternals ? true : false,
+ jforce ? true : false,
+ jignoreExternals ? true : false,
+ jignoreKeywords ? true : false,
EnumMapper::toDepth(jdepth), nativeEOL);
}
@@ -758,7 +815,7 @@ Java_org_apache_subversion_javahl_SVNClient_doImport
if (JNIUtil::isExceptionThrown())
return;
- RevpropTable revprops(jrevpropTable);
+ PropertyTable revprops(jrevpropTable, false, false);
if (JNIUtil::isExceptionThrown())
return;
@@ -796,11 +853,11 @@ Java_org_apache_subversion_javahl_SVNClient_suggestMergeSources
}
JNIEXPORT void JNICALL
-Java_org_apache_subversion_javahl_SVNClient_merge__Ljava_lang_String_2Lorg_apache_subversion_javahl_types_Revision_2Ljava_lang_String_2Lorg_apache_subversion_javahl_types_Revision_2Ljava_lang_String_2ZLorg_apache_subversion_javahl_types_Depth_2ZZZZ
+Java_org_apache_subversion_javahl_SVNClient_merge__Ljava_lang_String_2Lorg_apache_subversion_javahl_types_Revision_2Ljava_lang_String_2Lorg_apache_subversion_javahl_types_Revision_2Ljava_lang_String_2ZLorg_apache_subversion_javahl_types_Depth_2ZZZZZ
(JNIEnv *env, jobject jthis, jstring jpath1, jobject jrevision1,
jstring jpath2, jobject jrevision2, jstring jlocalPath, jboolean jforceDelete,
jobject jdepth, jboolean jignoreMergeinfo, jboolean jdiffIgnoreAncestry,
- jboolean jdryRun, jboolean jrecordOnly)
+ jboolean jdryRun, jboolean jallowMixedRev, jboolean jrecordOnly)
{
JNIEntry(SVNClient, merge);
SVNClient *cl = SVNClient::getCppObject(jthis);
@@ -835,15 +892,16 @@ Java_org_apache_subversion_javahl_SVNClient_merge__Ljava_lang_String_2Lorg_apach
jignoreMergeinfo ? true:false,
jdiffIgnoreAncestry ? true:false,
jdryRun ? true:false,
+ jallowMixedRev ? true:false,
jrecordOnly ? true:false);
}
JNIEXPORT void JNICALL
-Java_org_apache_subversion_javahl_SVNClient_merge__Ljava_lang_String_2Lorg_apache_subversion_javahl_types_Revision_2Ljava_util_List_2Ljava_lang_String_2ZLorg_apache_subversion_javahl_types_Depth_2ZZZZ
+Java_org_apache_subversion_javahl_SVNClient_merge__Ljava_lang_String_2Lorg_apache_subversion_javahl_types_Revision_2Ljava_util_List_2Ljava_lang_String_2ZLorg_apache_subversion_javahl_types_Depth_2ZZZZZ
(JNIEnv *env, jobject jthis, jstring jpath, jobject jpegRevision,
jobject jranges, jstring jlocalPath, jboolean jforceDelete, jobject jdepth,
jboolean jignoreMergeinfo, jboolean jdiffIgnoreAncestry,
- jboolean jdryRun, jboolean jrecordOnly)
+ jboolean jdryRun, jboolean jallowMixedRev, jboolean jrecordOnly)
{
JNIEntry(SVNClient, merge);
SVNClient *cl = SVNClient::getCppObject(jthis);
@@ -894,6 +952,7 @@ Java_org_apache_subversion_javahl_SVNClient_merge__Ljava_lang_String_2Lorg_apach
jignoreMergeinfo ? true:false,
jdiffIgnoreAncestry ? true:false,
jdryRun ? true:false,
+ jallowMixedRev ? true:false,
jrecordOnly ? true:false);
}
@@ -1009,7 +1068,7 @@ Java_org_apache_subversion_javahl_SVNClient_propertySetRemote
if (JNIUtil::isExceptionThrown())
return;
- RevpropTable revprops(jrevpropTable);
+ PropertyTable revprops(jrevpropTable, false, false);
if (JNIUtil::isExceptionThrown())
return;
@@ -1439,35 +1498,44 @@ Java_org_apache_subversion_javahl_SVNClient_diffSummarize__Ljava_lang_String_2Lo
jignoreAncestry ? true : false, receiver);
}
-JNIEXPORT void JNICALL
+JNIEXPORT jobject JNICALL
Java_org_apache_subversion_javahl_SVNClient_streamFileContent
-(JNIEnv *env, jobject jthis, jstring jpath, jobject jrevision,
- jobject jpegRevision, jobject jstream)
+(JNIEnv *env, jobject jthis, jstring jpath,
+ jobject jrevision, jobject jpegRevision,
+ jboolean jexpand_keywords, jboolean jreturn_props,
+ jobject jstream)
{
JNIEntry(SVNClient, streamFileContent);
SVNClient *cl = SVNClient::getCppObject(jthis);
if (cl == NULL)
{
JNIUtil::throwError(_("bad C++ this"));
- return;
+ return NULL;
}
JNIStringHolder path(jpath);
if (JNIUtil::isExceptionThrown())
- return;
+ return NULL;
Revision revision(jrevision);
if (JNIUtil::isExceptionThrown())
- return;
+ return NULL;
Revision pegRevision(jpegRevision);
if (JNIUtil::isExceptionThrown())
- return;
+ return NULL;
OutputStream dataOut(jstream);
if (JNIUtil::isExceptionThrown())
- return;
+ return NULL;
- cl->streamFileContent(path, revision, pegRevision, dataOut);
+ apr_hash_t* props =
+ cl->streamFileContent(path, revision, pegRevision,
+ bool(jexpand_keywords), bool(jreturn_props),
+ dataOut);
+ if (!jreturn_props || JNIUtil::isExceptionThrown())
+ return NULL;
+
+ return CreateJ::PropertyMap(props, SVN::Pool().getPool());
}
JNIEXPORT jstring JNICALL
@@ -1581,7 +1649,8 @@ JNIEXPORT void JNICALL
Java_org_apache_subversion_javahl_SVNClient_blame
(JNIEnv *env, jobject jthis, jstring jpath, jobject jpegRevision,
jobject jrevisionStart, jobject jrevisionEnd, jboolean jignoreMimeType,
- jboolean jincludeMergedRevisions, jobject jblameCallback)
+ jboolean jincludeMergedRevisions, jobject jblameCallback,
+ jobject jdiffOptions)
{
JNIEntry(SVNClient, blame);
SVNClient *cl = SVNClient::getCppObject(jthis);
@@ -1606,10 +1675,15 @@ Java_org_apache_subversion_javahl_SVNClient_blame
if (JNIUtil::isExceptionThrown())
return;
+ DiffOptions options(jdiffOptions);
+ if (JNIUtil::isExceptionThrown())
+ return;
+
BlameCallback callback(jblameCallback);
cl->blame(path, pegRevision, revisionStart, revisionEnd,
jignoreMimeType ? true : false,
- jincludeMergedRevisions ? true : false, &callback);
+ jincludeMergedRevisions ? true : false, &callback,
+ options);
}
JNIEXPORT void JNICALL
@@ -1648,6 +1722,36 @@ Java_org_apache_subversion_javahl_SVNClient_getConfigDirectory
}
JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_SVNClient_setConfigEventHandler(
+ JNIEnv* env, jobject jthis, jobject jcallback)
+{
+ JNIEntry(SVNClient, setConfigEventHandler);
+ SVNClient *cl = SVNClient::getCppObject(jthis);
+ if (cl == NULL)
+ {
+ JNIUtil::throwError(_("bad C++ this"));
+ return;
+ }
+
+ cl->getClientContext().setConfigEventHandler(jcallback);
+}
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_SVNClient_getConfigEventHandler(
+ JNIEnv* env, jobject jthis)
+{
+ JNIEntry(SVNClient, getConfigEventHandler);
+ SVNClient *cl = SVNClient::getCppObject(jthis);
+ if (cl == NULL)
+ {
+ JNIUtil::throwError(_("bad C++ this"));
+ return NULL;
+ }
+
+ return cl->getClientContext().getConfigEventHandler();
+}
+
+JNIEXPORT void JNICALL
Java_org_apache_subversion_javahl_SVNClient_cancelOperation
(JNIEnv *env, jobject jthis)
{
@@ -1738,8 +1842,9 @@ Java_org_apache_subversion_javahl_SVNClient_getChangelists
return;
ChangelistCallback callback(jchangelistCallback);
- cl->getChangelists(root_path, changelists, EnumMapper::toDepth(jdepth),
- &callback);
+ cl->getChangelists(root_path,
+ (jchangelists ? &changelists : NULL),
+ EnumMapper::toDepth(jdepth), &callback);
}
JNIEXPORT void JNICALL
@@ -1790,12 +1895,14 @@ Java_org_apache_subversion_javahl_SVNClient_unlock
}
JNIEXPORT void JNICALL
-Java_org_apache_subversion_javahl_SVNClient_info2
+Java_org_apache_subversion_javahl_SVNClient_info
(JNIEnv *env, jobject jthis, jstring jpath, jobject jrevision,
- jobject jpegRevision, jobject jdepth, jobject jchangelists,
- jobject jinfoCallback)
+ jobject jpegRevision, jobject jdepth,
+ jboolean jfetchExcluded, jboolean jfetchActualOnly,
+ jboolean jincludeExternals,
+ jobject jchangelists, jobject jinfoCallback)
{
- JNIEntry(SVNClient, info2);
+ JNIEntry(SVNClient, info);
SVNClient *cl = SVNClient::getCppObject(jthis);
if (cl == NULL)
{
@@ -1819,8 +1926,10 @@ Java_org_apache_subversion_javahl_SVNClient_info2
return;
InfoCallback callback(jinfoCallback);
- cl->info2(path, revision, pegRevision, EnumMapper::toDepth(jdepth),
- changelists, &callback);
+ cl->info(path, revision, pegRevision, EnumMapper::toDepth(jdepth),
+ svn_boolean_t(jfetchExcluded), svn_boolean_t(jfetchActualOnly),
+ svn_boolean_t(jincludeExternals),
+ changelists, &callback);
}
JNIEXPORT void JNICALL
@@ -1851,3 +1960,44 @@ Java_org_apache_subversion_javahl_SVNClient_patch
jreverse ? true : false, jignoreWhitespace ? true : false,
jremoveTempfiles ? true : false, &callback);
}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_SVNClient_vacuum
+(JNIEnv *env, jobject jthis, jstring jpath,
+ jboolean jremoveUnversionedItems, jboolean jremoveIgnoredItems,
+ jboolean jfixRecordedTimestamps, jboolean jremoveUnusedPristines,
+ jboolean jincludeExternals)
+{
+ JNIEntry(SVNClient, patch);
+ SVNClient *cl = SVNClient::getCppObject(jthis);
+ if (cl == NULL)
+ {
+ JNIUtil::throwError("bad C++ this");
+ return;
+ }
+
+ JNIStringHolder path(jpath);
+ cl->vacuum(path,
+ jremoveUnversionedItems, jremoveIgnoredItems,
+ jfixRecordedTimestamps, jremoveUnusedPristines,
+ jincludeExternals);
+}
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_SVNClient_nativeOpenRemoteSession
+(JNIEnv *env, jobject jthis, jstring jpath, jint jretryAttempts)
+{
+ JNIEntry(SVNClient, openRemoteSession);
+ SVNClient *cl = SVNClient::getCppObject(jthis);
+ if (cl == NULL)
+ {
+ JNIUtil::throwError("bad C++ this");
+ return NULL;
+ }
+
+ JNIStringHolder path(jpath);
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+
+ return cl->openRemoteSession(path, jretryAttempts);
+}
diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNRepos.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNRepos.cpp
index ee1c7c1..ae41bb3 100644
--- a/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNRepos.cpp
+++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNRepos.cpp
@@ -170,7 +170,7 @@ Java_org_apache_subversion_javahl_SVNRepos_dump
JNIEXPORT void JNICALL
Java_org_apache_subversion_javahl_SVNRepos_hotcopy
(JNIEnv *env, jobject jthis, jobject jpath, jobject jtargetPath,
- jboolean jcleanLogs, jboolean jincremental)
+ jboolean jcleanLogs, jboolean jincremental, jobject jnotifyCallback)
{
JNIEntry(SVNRepos, hotcopy);
SVNRepos *cl = SVNRepos::getCppObject(jthis);
@@ -188,8 +188,11 @@ Java_org_apache_subversion_javahl_SVNRepos_hotcopy
if (JNIUtil::isExceptionThrown())
return;
+ ReposNotifyCallback notifyCallback(jnotifyCallback);
+
cl->hotcopy(path, targetPath, jcleanLogs ? true : false,
- jincremental ? true : false);
+ jincremental ? true : false,
+ jnotifyCallback != NULL ? &notifyCallback : NULL);
}
JNIEXPORT void JNICALL
@@ -239,11 +242,13 @@ Java_org_apache_subversion_javahl_SVNRepos_listUnusedDBLogs
}
JNIEXPORT void JNICALL
-Java_org_apache_subversion_javahl_SVNRepos_load
-(JNIEnv *env, jobject jthis, jobject jpath, jobject jinputData,
- jobject jrevisionStart, jobject jrevisionEnd,
- jboolean jignoreUUID, jboolean jforceUUID, jboolean jusePreCommitHook,
- jboolean jusePostCommitHook, jstring jrelativePath, jobject jnotifyCallback)
+Java_org_apache_subversion_javahl_SVNRepos_load(
+ JNIEnv *env, jobject jthis, jobject jpath, jobject jinputData,
+ jobject jrevisionStart, jobject jrevisionEnd,
+ jboolean jignoreUUID, jboolean jforceUUID,
+ jboolean jusePreCommitHook, jboolean jusePostCommitHook,
+ jboolean jvalidateProps, jboolean jignoreDates,
+ jstring jrelativePath, jobject jnotifyCallback)
{
JNIEntry(SVNRepos, load);
SVNRepos *cl = SVNRepos::getCppObject(jthis);
@@ -280,6 +285,8 @@ Java_org_apache_subversion_javahl_SVNRepos_load
jforceUUID ? true : false,
jusePreCommitHook ? true : false,
jusePostCommitHook ? true : false,
+ jvalidateProps ? true : false,
+ jignoreDates ? true : false,
relativePath,
(jnotifyCallback != NULL ? &notifyCallback : NULL));
}
@@ -409,9 +416,11 @@ Java_org_apache_subversion_javahl_SVNRepos_setRevProp
}
JNIEXPORT void JNICALL
-Java_org_apache_subversion_javahl_SVNRepos_verify
-(JNIEnv *env, jobject jthis, jobject jpath, jobject jrevisionStart,
- jobject jrevisionEnd, jobject jcallback)
+Java_org_apache_subversion_javahl_SVNRepos_verify(
+ JNIEnv *env, jobject jthis, jobject jpath,
+ jobject jrevisionStart, jobject jrevisionEnd,
+ jboolean jcheckNormalization, jboolean jmetadataOnly,
+ jobject jnotifyCallback, jobject jverifyCallback)
{
JNIEntry(SVNRepos, verify);
SVNRepos *cl = SVNRepos::getCppObject(jthis);
@@ -433,12 +442,18 @@ Java_org_apache_subversion_javahl_SVNRepos_verify
if (JNIUtil::isExceptionThrown())
return;
- ReposNotifyCallback callback(jcallback);
+ ReposNotifyCallback notify_cb(jnotifyCallback);
+ if (JNIUtil::isExceptionThrown())
+ return;
+
+ ReposVerifyCallback verify_cb(jverifyCallback);
if (JNIUtil::isExceptionThrown())
return;
cl->verify(path, revisionStart, revisionEnd,
- jcallback != NULL ? &callback : NULL);
+ jcheckNormalization, jmetadataOnly,
+ jnotifyCallback != NULL ? &notify_cb : NULL,
+ jverifyCallback != NULL ? &verify_cb : NULL);
}
JNIEXPORT jobject JNICALL
diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_CommitEditor.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_CommitEditor.cpp
new file mode 100644
index 0000000..e871b0c
--- /dev/null
+++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_CommitEditor.cpp
@@ -0,0 +1,205 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file org_apache_subversion_javahl_remote_CommitEditor.cpp
+ * @brief Implementation of the native methods in the Java class CommitEditor
+ */
+
+#include "../include/org_apache_subversion_javahl_remote_CommitEditor.h"
+
+#include "JNIStackElement.h"
+#include "JNIUtil.h"
+#include "CommitEditor.h"
+
+#include "svn_private_config.h"
+
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_CommitEditor_finalize(
+ JNIEnv *env, jobject jthis)
+{
+ JNIEntry(CommitEditor, finalize);
+ CommitEditor *editor = CommitEditor::getCppObject(jthis);
+ if (editor != NULL)
+ editor->finalize();
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_CommitEditor_nativeDispose(
+ JNIEnv *env, jobject jthis)
+{
+ JNIEntry(CommitEditor, nativeDispose);
+ CommitEditor *editor = CommitEditor::getCppObject(jthis);
+ if (editor != NULL)
+ editor->dispose(jthis);
+}
+
+JNIEXPORT jlong JNICALL
+Java_org_apache_subversion_javahl_remote_CommitEditor_nativeCreateInstance(
+ JNIEnv *env, jclass thisclass, jobject jsession, jobject jrevprops,
+ jobject jcommit_callback, jobject jlock_tokens, jboolean jkeep_locks,
+ jobject jget_base_cb, jobject jget_props_cb, jobject jget_kind_cb)
+{
+ jobject jthis = NULL; // Placeholder -- this is a static method
+ JNIEntry(CommitEditor, nativeCreateInstance);
+
+ return CommitEditor::createInstance(
+ jsession, jrevprops, jcommit_callback, jlock_tokens, jkeep_locks,
+ jget_base_cb, jget_props_cb, jget_kind_cb);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_CommitEditor_addDirectory(
+ JNIEnv* env, jobject jthis,
+ jstring jrelpath, jobject jchildren, jobject jproperties,
+ jlong jreplaces_revision)
+{
+ JNIEntry(CommitEditor, addDirectory);
+ CommitEditor *editor = CommitEditor::getCppObject(jthis);
+ CPPADDR_NULL_PTR(editor,);
+ editor->addDirectory(jrelpath, jchildren, jproperties, jreplaces_revision);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_CommitEditor_addFile(
+ JNIEnv* env, jobject jthis,
+ jstring jrelpath, jobject jchecksum, jobject jcontents,
+ jobject jproperties, jlong jreplaces_revision)
+{
+ JNIEntry(CommitEditor, addFile);
+ CommitEditor *editor = CommitEditor::getCppObject(jthis);
+ CPPADDR_NULL_PTR(editor,);
+ editor->addFile(jrelpath, jchecksum, jcontents, jproperties,
+ jreplaces_revision);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_CommitEditor_addSymlink(
+ JNIEnv* env, jobject jthis,
+ jstring jrelpath, jstring jtarget, jobject jproperties,
+ jlong jreplaces_revision)
+{
+ JNIEntry(CommitEditor, addSymlink);
+ CommitEditor *editor = CommitEditor::getCppObject(jthis);
+ CPPADDR_NULL_PTR(editor,);
+ editor->addSymlink(jrelpath, jtarget, jproperties, jreplaces_revision);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_CommitEditor_addAbsent(
+ JNIEnv* env, jobject jthis,
+ jstring jrelpath, jobject jkind, jlong jreplaces_revision)
+{
+ JNIEntry(CommitEditor, addAbsent);
+ CommitEditor *editor = CommitEditor::getCppObject(jthis);
+ CPPADDR_NULL_PTR(editor,);
+ editor->addAbsent(jrelpath, jkind, jreplaces_revision);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_CommitEditor_alterDirectory(
+ JNIEnv* env, jobject jthis,
+ jstring jrelpath, jlong jrevision, jobject jchildren, jobject jproperties)
+{
+ JNIEntry(CommitEditor, alterDirectory);
+ CommitEditor *editor = CommitEditor::getCppObject(jthis);
+ CPPADDR_NULL_PTR(editor,);
+ editor->alterDirectory(jrelpath, jrevision, jchildren, jproperties);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_CommitEditor_alterFile(
+ JNIEnv* env, jobject jthis,
+ jstring jrelpath, jlong jrevision, jobject jchecksum, jobject jcontents,
+ jobject jproperties)
+{
+ JNIEntry(CommitEditor, alterFile);
+ CommitEditor *editor = CommitEditor::getCppObject(jthis);
+ CPPADDR_NULL_PTR(editor,);
+ editor->alterFile(jrelpath, jrevision, jchecksum, jcontents, jproperties);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_CommitEditor_alterSymlink(
+ JNIEnv* env, jobject jthis,
+ jstring jrelpath, jlong jrevision, jstring jtarget, jobject jproperties)
+{
+ JNIEntry(CommitEditor, alterSymlink);
+ CommitEditor *editor = CommitEditor::getCppObject(jthis);
+ CPPADDR_NULL_PTR(editor,);
+ editor->alterSymlink(jrelpath, jrevision, jtarget, jproperties);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_CommitEditor_delete(
+ JNIEnv* env, jobject jthis,
+ jstring jrelpath, jlong jrevision)
+{
+ JNIEntry(CommitEditor, delete);
+ CommitEditor *editor = CommitEditor::getCppObject(jthis);
+ CPPADDR_NULL_PTR(editor,);
+ editor->remove(jrelpath, jrevision);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_CommitEditor_copy(
+ JNIEnv* env, jobject jthis,
+ jstring jsrc_relpath, jlong jsrc_revision, jstring jdst_relpath,
+ jlong jreplaces_revision)
+{
+ JNIEntry(CommitEditor, copy);
+ CommitEditor *editor = CommitEditor::getCppObject(jthis);
+ CPPADDR_NULL_PTR(editor,);
+ editor->copy(jsrc_relpath, jsrc_revision, jdst_relpath, jreplaces_revision);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_CommitEditor_move(
+ JNIEnv* env, jobject jthis,
+ jstring jsrc_relpath, jlong jsrc_revision, jstring jdst_relpath,
+ jlong jreplaces_revision)
+{
+ JNIEntry(CommitEditor, move);
+ CommitEditor *editor = CommitEditor::getCppObject(jthis);
+ CPPADDR_NULL_PTR(editor,);
+ editor->move(jsrc_relpath, jsrc_revision, jdst_relpath, jreplaces_revision);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_CommitEditor_complete(
+ JNIEnv* env, jobject jthis)
+{
+ JNIEntry(CommitEditor, complete);
+ CommitEditor *editor = CommitEditor::getCppObject(jthis);
+ CPPADDR_NULL_PTR(editor,);
+ editor->complete();
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_CommitEditor_abort(
+ JNIEnv* env, jobject jthis, jobject jsession)
+{
+ JNIEntry(CommitEditor, abort);
+ CommitEditor *editor = CommitEditor::getCppObject(jthis);
+ CPPADDR_NULL_PTR(editor,);
+ editor->abort();
+}
diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteFactory.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteFactory.cpp
new file mode 100644
index 0000000..80c0c42
--- /dev/null
+++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteFactory.cpp
@@ -0,0 +1,61 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file org_apache_subversion_javahl_remote_RemoteFactory.cpp
+ * @brief Implementation of the native methods in the Java class RemoteFactory
+ */
+
+#include "../include/org_apache_subversion_javahl_remote_RemoteFactory.h"
+
+#include "JNIStackElement.h"
+#include "JNIUtil.h"
+#include "JNIStringHolder.h"
+
+#include "RemoteSession.h"
+
+#include "svn_private_config.h"
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteFactory_open(
+ JNIEnv *env, jclass jclazz, jint jretryAttempts,
+ jstring jurl, jstring juuid,
+ jstring jconfigDirectory,
+ jstring jusername, jstring jpassword,
+ jobject jprompter, jobject jdeprecatedPrompter,
+ jobject jprogress, jobject jcfgcb, jobject jtunnelcb)
+{
+ //JNI macros need jthis but this is a static call
+ JNIEntryStatic(RemoteFactory, open);
+
+ /*
+ * Create RemoteSession C++ object and return its java wrapper to the caller
+ */
+ jobject jremoteSession = RemoteSession::open(
+ jretryAttempts, jurl, juuid,
+ jconfigDirectory, jusername, jpassword,
+ jprompter, jdeprecatedPrompter,
+ jprogress, jcfgcb, jtunnelcb);
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+
+ return jremoteSession;
+}
diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteSession.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteSession.cpp
new file mode 100644
index 0000000..03079b6
--- /dev/null
+++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteSession.cpp
@@ -0,0 +1,359 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file org_apache_subversion_javahl_remote_RemoteSession.cpp
+ * @brief Implementation of the native methods in the Java class RemoteSession
+ */
+
+#include "../include/org_apache_subversion_javahl_remote_RemoteSession.h"
+
+#include "JNIStackElement.h"
+#include "JNIUtil.h"
+#include "Prompter.h"
+#include "RemoteSession.h"
+#include "Revision.h"
+#include "EnumMapper.h"
+
+#include "svn_private_config.h"
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_finalize(
+ JNIEnv *env, jobject jthis)
+{
+ JNIEntry(RemoteSession, finalize);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ if (ras != NULL)
+ ras->finalize();
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_nativeDispose(
+ JNIEnv *env, jobject jthis)
+{
+ JNIEntry(RemoteSession, nativeDispose);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ if (ras != NULL)
+ ras->dispose(jthis);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_cancelOperation(
+ JNIEnv *env, jobject jthis)
+{
+ JNIEntry(RemoteSession, cancelOperation);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, );
+
+ ras->cancelOperation();
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_reparent(
+ JNIEnv *env, jobject jthis, jstring jurl)
+{
+ JNIEntry(RemoteSession, getSessionUrl);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, );
+
+ ras->reparent(jurl);
+}
+
+JNIEXPORT jstring JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_getSessionRelativePath(
+ JNIEnv *env, jobject jthis, jstring jurl)
+{
+ JNIEntry(RemoteSession, getSessionUrl);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, NULL);
+
+ return ras->getSessionRelativePath(jurl);
+}
+
+JNIEXPORT jstring JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_getReposRelativePath(
+ JNIEnv *env, jobject jthis, jstring jurl)
+{
+ JNIEntry(RemoteSession, getSessionUrl);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, NULL);
+
+ return ras->getReposRelativePath(jurl);
+}
+
+JNIEXPORT jstring JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_getSessionUrl(
+ JNIEnv *env, jobject jthis)
+{
+ JNIEntry(RemoteSession, getSessionUrl);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, NULL);
+
+ return ras->getSessionUrl();
+}
+
+JNIEXPORT jstring JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_getReposUUID(
+ JNIEnv *env, jobject jthis)
+{
+ JNIEntry(RemoteSession, geRepostUUID);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, NULL);
+
+ return ras->getReposUUID();
+}
+
+JNIEXPORT jstring JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_getReposRootUrl(
+ JNIEnv *env, jobject jthis)
+{
+ JNIEntry(RemoteSession, geRepostUUID);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, NULL);
+
+ return ras->getReposRootUrl();
+}
+
+JNIEXPORT jlong JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_getLatestRevision(
+ JNIEnv *env, jobject jthis)
+{
+ JNIEntry(RemoteSession, getLatestRevision);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, SVN_INVALID_REVNUM);
+
+ return ras->getLatestRevision();
+}
+
+JNIEXPORT jlong JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_getRevisionByTimestamp(
+ JNIEnv *env, jobject jthis, jlong timestamp)
+{
+ JNIEntry(RemoteSession, getDatedRevision);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, SVN_INVALID_REVNUM);
+
+ return ras->getRevisionByTimestamp(timestamp);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_nativeChangeRevisionProperty(
+ JNIEnv *env, jobject jthis, jlong jrevision, jstring jname,
+ jbyteArray jold_value, jbyteArray jvalue)
+{
+ JNIEntry(RemoteSession, nativeChangeRevisionProperty);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, );
+
+ return ras->changeRevisionProperty(jrevision, jname, jold_value, jvalue);
+}
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_getRevisionProperties(
+ JNIEnv *env, jobject jthis, jlong jrevision)
+{
+ JNIEntry(SVNReposAccess, getRevisionProperties);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, NULL);
+
+ return ras->getRevisionProperties(jrevision);
+}
+
+JNIEXPORT jbyteArray JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_getRevisionProperty(
+ JNIEnv *env, jobject jthis, jlong jrevision, jstring jname)
+{
+ JNIEntry(SVNReposAccess, getRevisionProperty);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, NULL);
+
+ return ras->getRevisionProperty(jrevision, jname);
+}
+
+JNIEXPORT jlong JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_nativeGetFile(
+ JNIEnv *env, jobject jthis, jlong jrevision, jstring jpath,
+ jobject jcontents, jobject jproperties)
+{
+ JNIEntry(SVNReposAccess, nativeGetFile);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, SVN_INVALID_REVNUM);
+
+ return ras->getFile(jrevision, jpath, jcontents, jproperties);
+}
+
+JNIEXPORT jlong JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_nativeGetDirectory(
+ JNIEnv *env, jobject jthis, jlong jrevision, jstring jpath,
+ jint jdirent_fields, jobject jdirents, jobject jproperties)
+{
+ JNIEntry(SVNReposAccess, nativeGetDirectory);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, SVN_INVALID_REVNUM);
+
+ return ras->getDirectory(jrevision, jpath,
+ jdirent_fields, jdirents, jproperties);
+}
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_getMergeinfo(
+ JNIEnv *env, jobject jthis, jobject jpaths, jlong jrevision,
+ jobject jinherit, jboolean jinclude_descendants)
+{
+ JNIEntry(SVNReposAccess, getMergeinfo);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, NULL);
+
+ return ras->getMergeinfo(jpaths, jrevision, jinherit, jinclude_descendants);
+}
+
+// TODO: update
+// TODO: switch
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_nativeStatus(
+ JNIEnv *env, jobject jthis, jstring jstatus_target, jlong jrevision,
+ jobject jdepth, jobject jstatus_editor, jobject jreporter)
+{
+ JNIEntry(SVNReposAccess, nativeStatus);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras,);
+
+ ras->status(jthis, jstatus_target, jrevision, jdepth,
+ jstatus_editor, jreporter);
+}
+
+// TODO: diff
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_getLog(
+ JNIEnv *env, jobject jthis, jobject jpaths,
+ jlong jstartrev, jlong jendrev, jint jlimit,
+ jboolean jstrict_node_history, jboolean jdiscover_changed_paths,
+ jboolean jinclude_merged_revisions,
+ jobject jrevprops, jobject jlog_callback)
+{
+ JNIEntry(SVNReposAccess, getLog);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras,);
+
+ ras->getLog(jpaths, jstartrev, jendrev, jlimit,
+ jstrict_node_history, jdiscover_changed_paths,
+ jinclude_merged_revisions,
+ jrevprops, jlog_callback);
+}
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_checkPath(
+ JNIEnv *env, jobject jthis, jstring jpath, jlong jrevision)
+{
+ JNIEntry(SVNReposAccess, checkPath);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, NULL);
+
+ return ras->checkPath(jpath, jrevision);
+}
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_stat(
+ JNIEnv *env, jobject jthis, jstring jpath, jlong jrevision)
+{
+ JNIEntry(SVNReposAccess, stat);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, NULL);
+
+ return ras->stat(jpath, jrevision);
+}
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_getLocations(
+ JNIEnv *env, jobject jthis, jstring jpath, jlong jpeg_revision,
+ jobject jlocation_revisions)
+{
+ JNIEntry(SVNReposAccess, getLocations);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, NULL);
+
+ return ras->getLocations(jpath, jpeg_revision, jlocation_revisions);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_getLocationSegments(
+ JNIEnv *env, jobject jthis, jstring jpath, jlong jpeg_revision,
+ jlong jstart_revision, jlong jend_revision, jobject jcallback)
+{
+ JNIEntry(SVNReposAccess, getLocationSegments);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, );
+
+ if (jcallback == NULL)
+ JNIUtil::throwNullPointerException("handler");
+ ras->getLocationSegments(jpath, jpeg_revision,
+ jstart_revision, jend_revision,
+ jcallback);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_getFileRevisions(
+ JNIEnv *env, jobject jthis, jstring jpath,
+ jlong jstart_revision, jlong jend_revision,
+ jboolean jinclude_merged_revisions, jobject jcallback)
+{
+ JNIEntry(SVNReposAccess, getFileRevisions);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, );
+
+ if (jcallback == NULL)
+ JNIUtil::throwNullPointerException("handler");
+ ras->getFileRevisions(jpath, jstart_revision, jend_revision,
+ jinclude_merged_revisions, jcallback);
+}
+
+// TODO: lock
+// TODO: unlock
+// TODO: getLock
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_getLocks(
+ JNIEnv *env, jobject jthis, jstring jpath, jobject jdepth)
+{
+ JNIEntry(RemoteSession, getLocks);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, NULL);
+
+ return ras->getLocks(jpath, jdepth);
+}
+
+// TODO: replayRange
+// TODO: replay
+// TODO: getDeletedRevision
+// TODO: getInheritedProperties
+
+JNIEXPORT jboolean JNICALL
+Java_org_apache_subversion_javahl_remote_RemoteSession_nativeHasCapability(
+ JNIEnv *env, jobject jthis, jstring jcapability)
+{
+ JNIEntry(RemoteSession, nativeHasCapability);
+ RemoteSession *ras = RemoteSession::getCppObject(jthis);
+ CPPADDR_NULL_PTR(ras, false);
+
+ return ras->hasCapability(jcapability);
+}
diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_StateReporter.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_StateReporter.cpp
new file mode 100644
index 0000000..f5c291b
--- /dev/null
+++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_StateReporter.cpp
@@ -0,0 +1,117 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file org_apache_subversion_javahl_remote_StateReporter.cpp
+ * @brief Implementation of the native methods in the Java class StateReporter
+ */
+
+#include "../include/org_apache_subversion_javahl_remote_StateReporter.h"
+
+#include <jni.h>
+#include "JNIStackElement.h"
+#include "JNIUtil.h"
+#include "StateReporter.h"
+
+#include "svn_private_config.h"
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_StateReporter_finalize(
+ JNIEnv *env, jobject jthis)
+{
+ JNIEntry(StateReporter, finalize);
+ StateReporter *reporter = StateReporter::getCppObject(jthis);
+ if (reporter != NULL)
+ reporter->finalize();
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_StateReporter_nativeDispose(
+ JNIEnv* env, jobject jthis)
+{
+ JNIEntry(StateReporter, nativeCreateInstance);
+ StateReporter* reporter = StateReporter::getCppObject(jthis);
+ CPPADDR_NULL_PTR(reporter,);
+ reporter->dispose(jthis);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_StateReporter_setPath(
+ JNIEnv* env, jobject jthis,
+ jstring jpath, jlong jrevision, jobject jdepth,
+ jboolean jstart_empty, jstring jlock_token)
+{
+ JNIEntry(StateReporter, setPath);
+ StateReporter* reporter = StateReporter::getCppObject(jthis);
+ CPPADDR_NULL_PTR(reporter,);
+ reporter->setPath(jpath, jrevision, jdepth, jstart_empty, jlock_token);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_StateReporter_deletePath(
+ JNIEnv* env, jobject jthis, jstring jpath)
+{
+ JNIEntry(StateReporter, deletePath);
+ StateReporter* reporter = StateReporter::getCppObject(jthis);
+ CPPADDR_NULL_PTR(reporter,);
+ reporter->deletePath(jpath);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_StateReporter_linkPath(
+ JNIEnv* env, jobject jthis,
+ jstring jurl, jstring jpath, jlong jrevision, jobject jdepth,
+ jboolean jstart_empty, jstring jlock_token)
+{
+ JNIEntry(StateReporter, linkPath);
+ StateReporter* reporter = StateReporter::getCppObject(jthis);
+ CPPADDR_NULL_PTR(reporter,);
+ reporter->linkPath(jurl, jpath, jrevision, jdepth, jstart_empty, jlock_token);
+}
+
+JNIEXPORT jlong JNICALL
+Java_org_apache_subversion_javahl_remote_StateReporter_finishReport(
+ JNIEnv* env, jobject jthis)
+{
+ JNIEntry(StateReporter, finishReport);
+ StateReporter* reporter = StateReporter::getCppObject(jthis);
+ CPPADDR_NULL_PTR(reporter, SVN_INVALID_REVNUM);
+ return reporter->finishReport();
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_remote_StateReporter_abortReport(
+ JNIEnv* env, jobject jthis)
+{
+ JNIEntry(StateReporter, abortReport);
+ StateReporter* reporter = StateReporter::getCppObject(jthis);
+ CPPADDR_NULL_PTR(reporter,);
+ reporter->abortReport();
+}
+
+JNIEXPORT jlong JNICALL
+Java_org_apache_subversion_javahl_remote_StateReporter_nativeCreateInstance(
+ JNIEnv* env, jclass clazz)
+{
+ jobject jthis = NULL;
+ JNIEntry(StateReporter, nativeCreateInstance);
+ return reinterpret_cast<jlong>(new StateReporter);
+}
diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RevisionRangeList.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RevisionRangeList.cpp
new file mode 100644
index 0000000..4c54118
--- /dev/null
+++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RevisionRangeList.cpp
@@ -0,0 +1,58 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file org_apache_subversion_javahl_types_RevisionRangeList.cpp
+ * @brief Implementation of the native methods in the Java class
+ * RevisionRangeList.
+ */
+
+
+#include "../include/org_apache_subversion_javahl_types_RevisionRangeList.h"
+#include "JNIStackElement.h"
+#include "RevisionRangeList.h"
+#include "Pool.h"
+
+#include "svn_mergeinfo.h"
+#include "svn_private_config.h"
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_types_RevisionRangeList_remove(
+ JNIEnv* env, jobject jthis, jobject jeraser,
+ jboolean jconsider_inheritance)
+{
+ JNIEntry(RevisionRangeList, remove);
+
+ SVN::Pool request_pool;
+
+ RevisionRangeList rangelist = RevisionRangeList::create(jthis, request_pool);
+ CPPADDR_NULL_PTR(rangelist.get(), NULL);
+
+ RevisionRangeList eraser(jeraser, request_pool);
+ CPPADDR_NULL_PTR(eraser.get(), NULL);
+
+ svn_rangelist_t *output;
+ SVN_JNI_ERR(svn_rangelist_remove(&output, eraser.get(), rangelist.get(),
+ bool(jconsider_inheritance),
+ request_pool.getPool()),
+ NULL);
+ return RevisionRangeList(output).toList();
+}
diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RuntimeVersion.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RuntimeVersion.cpp
new file mode 100644
index 0000000..ed43fb7
--- /dev/null
+++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RuntimeVersion.cpp
@@ -0,0 +1,69 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file org_apache_subversion_javahl_types_RuntimeVersion.cpp
+ * @brief Implementation of the native methods in the Java class RuntimeVersion.
+ */
+
+#include "../include/org_apache_subversion_javahl_types_RuntimeVersion.h"
+#include "svn_client.h"
+#include "svn_version.h"
+
+#include "jniwrapper/jni_stack.hpp"
+#include "jniwrapper/jni_string.hpp"
+
+JNIEXPORT jint JNICALL
+Java_org_apache_subversion_javahl_types_RuntimeVersion_getMajor(
+ JNIEnv* jenv, jobject jthis)
+{
+ const svn_version_t* const version = svn_client_version();
+ return jint(version->major);
+}
+
+JNIEXPORT jint JNICALL
+Java_org_apache_subversion_javahl_types_RuntimeVersion_getMinor(
+ JNIEnv* jenv, jobject jthis)
+{
+ const svn_version_t* const version = svn_client_version();
+ return jint(version->minor);
+}
+
+JNIEXPORT jint JNICALL
+Java_org_apache_subversion_javahl_types_RuntimeVersion_getPatch(
+ JNIEnv* jenv, jobject jthis)
+{
+ const svn_version_t* const version = svn_client_version();
+ return jint(version->patch);
+}
+
+JNIEXPORT jstring JNICALL
+Java_org_apache_subversion_javahl_types_RuntimeVersion_getNumberTag(
+ JNIEnv* jenv, jobject jthis)
+{
+ SVN_JAVAHL_JNI_TRY(RuntimeVersion, getNumberTag)
+ {
+ const svn_version_t* const version = svn_client_version();
+ return Java::String(Java::Env(jenv), version->tag).get();
+ }
+ SVN_JAVAHL_JNI_CATCH;
+ return NULL;
+}
diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_Version.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_Version.cpp
index 8fd8aee..72d52a3 100644
--- a/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_Version.cpp
+++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_Version.cpp
@@ -28,12 +28,19 @@
#include "JNIStackElement.h"
#include "svn_version.h"
+namespace {
+const svn_version_t* javahl_version()
+{
+ SVN_VERSION_BODY;
+}
+} //anonymous namespace
+
JNIEXPORT jint JNICALL
Java_org_apache_subversion_javahl_types_Version_getMajor(JNIEnv *env,
jobject jthis)
{
JNIEntry(Version, getMajor);
- return SVN_VER_MAJOR;
+ return javahl_version()->major;
}
JNIEXPORT jint JNICALL
@@ -41,7 +48,7 @@ Java_org_apache_subversion_javahl_types_Version_getMinor(JNIEnv *env,
jobject jthis)
{
JNIEntry(Version, getMinor);
- return SVN_VER_MINOR;
+ return javahl_version()->minor;
}
JNIEXPORT jint JNICALL
@@ -49,7 +56,7 @@ Java_org_apache_subversion_javahl_types_Version_getPatch(JNIEnv *env,
jobject jthis)
{
JNIEntry(Version, getPatch);
- return SVN_VER_PATCH;
+ return javahl_version()->patch;
}
JNIEXPORT jstring JNICALL
diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_VersionExtended.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_VersionExtended.cpp
index e24801a..b1ecd72 100644
--- a/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_VersionExtended.cpp
+++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_VersionExtended.cpp
@@ -33,8 +33,24 @@
#include "JNIStackElement.h"
#include <string>
+#include "svn_private_config.h"
+
// VersionExtended native methods
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_types_VersionExtended_dispose(
+ JNIEnv *env, jobject jthis)
+{
+ JNIEntry(VersionExtended, dispose);
+ VersionExtended *const vx = VersionExtended::getCppObject(jthis);
+ if (vx == NULL)
+ {
+ JNIUtil::throwError(_("bad C++ this"));
+ return;
+ }
+ vx->dispose(jthis);
+}
+
JNIEXPORT jstring JNICALL
Java_org_apache_subversion_javahl_types_VersionExtended_getBuildDate(
JNIEnv *env, jobject jthis)
diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigImpl_Category.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigImpl_Category.cpp
new file mode 100644
index 0000000..5fa5c89
--- /dev/null
+++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigImpl_Category.cpp
@@ -0,0 +1,314 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file org_apache_subversion_javahl_util_ConfigImpl_Category.cpp
+ * @brief Implementation of the native methods in the Java class
+ * util.ConfigImpl.Category.
+ */
+
+#include <string>
+#include <vector>
+
+#include "../include/org_apache_subversion_javahl_util_ConfigImpl_Category.h"
+#include "JNIUtil.h"
+#include "JNIStackElement.h"
+#include "JNIStringHolder.h"
+#include "OperationContext.h"
+#include "CreateJ.h"
+#include "EnumMapper.h"
+
+#include "svn_config.h"
+#include "svn_hash.h"
+#include "svn_private_config.h"
+
+namespace {
+struct ImplContext
+{
+ ImplContext(JNIEnv* env, jobject jthis,
+ jstring jcategory, jlong jcontext,
+ jstring jsection, jstring joption) : m_config(NULL)
+ {
+ OperationContext* const context(
+ reinterpret_cast<OperationContext*>(jcontext));
+ CPPADDR_NULL_PTR(context,);
+
+ JNIStringHolder category(jcategory);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ if (category.c_str())
+ {
+ apr_hash_t* cfgdata = context->getConfigData();
+ if (cfgdata)
+ m_config = static_cast<svn_config_t*>(
+ svn_hash_gets(cfgdata, category.c_str()));
+ else
+ JNIUtil::throwNullPointerException("getConfigData");
+ }
+ if (!m_config)
+ JNIUtil::throwNullPointerException("category");
+
+ JNIStringHolder section(jsection);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ if (section.c_str())
+ m_section = section.c_str();
+
+ JNIStringHolder option(joption);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+ if (option.c_str())
+ m_option = option.c_str();
+ }
+
+ svn_config_t* m_config;
+ std::string m_section;
+ std::string m_option;
+};
+} // anonymous namespace
+
+
+
+JNIEXPORT jstring JNICALL
+Java_org_apache_subversion_javahl_util_ConfigImpl_00024Category_get_1str(
+ JNIEnv* env, jobject jthis, jstring jcategory, jlong jcontext,
+ jstring jsection, jstring joption, jstring jdefault_value)
+{
+ JNIEntry(ConfigImpl$Category, get_str);
+ const ImplContext ctx(env, jthis, jcategory, jcontext, jsection, joption);
+
+ JNIStringHolder default_value(jdefault_value);
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+
+ const char* value;
+ svn_config_get(ctx.m_config, &value,
+ ctx.m_section.c_str(), ctx.m_option.c_str(),
+ default_value.c_str());
+ return JNIUtil::makeJString(value);
+}
+
+JNIEXPORT jboolean JNICALL
+Java_org_apache_subversion_javahl_util_ConfigImpl_00024Category_get_1bool(
+ JNIEnv* env, jobject jthis, jstring jcategory, jlong jcontext,
+ jstring jsection, jstring joption, jboolean jdefault_value)
+{
+ JNIEntry(ConfigImpl$Category, get_bool);
+ const ImplContext ctx(env, jthis, jcategory, jcontext, jsection, joption);
+
+ svn_boolean_t value;
+ SVN_JNI_ERR(svn_config_get_bool(ctx.m_config, &value,
+ ctx.m_section.c_str(), ctx.m_option.c_str(),
+ bool(jdefault_value)),
+ jdefault_value);
+ return jboolean(value);
+}
+
+JNIEXPORT jlong JNICALL
+Java_org_apache_subversion_javahl_util_ConfigImpl_00024Category_get_1long(
+ JNIEnv* env, jobject jthis, jstring jcategory, jlong jcontext,
+ jstring jsection, jstring joption, jlong jdefault_value)
+{
+ JNIEntry(ConfigImpl$Category, get_long);
+ const ImplContext ctx(env, jthis, jcategory, jcontext, jsection, joption);
+
+ apr_int64_t value;
+ SVN_JNI_ERR(svn_config_get_int64(ctx.m_config, &value,
+ ctx.m_section.c_str(), ctx.m_option.c_str(),
+ apr_int64_t(jdefault_value)),
+ jdefault_value);
+ return jlong(value);
+}
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_util_ConfigImpl_00024Category_get_1tri(
+ JNIEnv* env, jobject jthis, jstring jcategory, jlong jcontext,
+ jstring jsection, jstring joption,
+ jstring junknown, jobject jdefault_value)
+{
+ JNIEntry(ConfigImpl$Category, get_tri);
+ const ImplContext ctx(env, jthis, jcategory, jcontext, jsection, joption);
+
+ JNIStringHolder unknown(junknown);
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+
+ svn_tristate_t value;
+ SVN_JNI_ERR(svn_config_get_tristate(ctx.m_config, &value,
+ ctx.m_section.c_str(),
+ ctx.m_option.c_str(),
+ unknown.c_str(),
+ EnumMapper::toTristate(jdefault_value)),
+ NULL);
+ return EnumMapper::mapTristate(value);
+}
+
+JNIEXPORT jstring JNICALL
+Java_org_apache_subversion_javahl_util_ConfigImpl_00024Category_get_1yna(
+ JNIEnv* env, jobject jthis, jstring jcategory, jlong jcontext,
+ jstring jsection, jstring joption, jstring jdefault_value)
+{
+ JNIEntry(ConfigImpl$Category, get_yna);
+ const ImplContext ctx(env, jthis, jcategory, jcontext, jsection, joption);
+
+ JNIStringHolder default_value(jdefault_value);
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+
+ const char* value;
+ SVN_JNI_ERR(svn_config_get_yes_no_ask(
+ ctx.m_config, &value,
+ ctx.m_section.c_str(), ctx.m_option.c_str(),
+ default_value.c_str()),
+ NULL);
+ return JNIUtil::makeJString(value);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_util_ConfigImpl_00024Category_set_1str(
+ JNIEnv* env, jobject jthis, jstring jcategory, jlong jcontext,
+ jstring jsection, jstring joption, jstring jvalue)
+{
+ JNIEntry(ConfigImpl$Category, set_str);
+ const ImplContext ctx(env, jthis, jcategory, jcontext, jsection, joption);
+
+ JNIStringHolder value(jvalue);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ svn_config_set(ctx.m_config,
+ ctx.m_section.c_str(), ctx.m_option.c_str(),
+ value.c_str());
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_util_ConfigImpl_00024Category_set_1bool(
+ JNIEnv* env, jobject jthis, jstring jcategory, jlong jcontext,
+ jstring jsection, jstring joption, jboolean jvalue)
+{
+ JNIEntry(ConfigImpl$Category, set_bool);
+ const ImplContext ctx(env, jthis, jcategory, jcontext, jsection, joption);
+
+ svn_config_set_bool(ctx.m_config,
+ ctx.m_section.c_str(), ctx.m_option.c_str(),
+ bool(jvalue));
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_util_ConfigImpl_00024Category_set_1long(
+ JNIEnv* env, jobject jthis, jstring jcategory, jlong jcontext,
+ jstring jsection, jstring joption, jlong jvalue)
+{
+ JNIEntry(ConfigImpl$Category, set_long);
+ const ImplContext ctx(env, jthis, jcategory, jcontext, jsection, joption);
+
+ svn_config_set_int64(ctx.m_config,
+ ctx.m_section.c_str(), ctx.m_option.c_str(),
+ apr_int64_t(jvalue));
+}
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_util_ConfigImpl_00024Category_sections(
+ JNIEnv* env, jobject jthis, jstring jcategory, jlong jcontext)
+{
+ JNIEntry(ConfigImpl$Category, sections);
+ const ImplContext ctx(env, jthis, jcategory, jcontext, NULL, NULL);
+
+ struct enumerator_t
+ {
+ static svn_boolean_t process(const char* name, void* baton,
+ apr_pool_t *pool)
+ {
+ jstring jname = JNIUtil::makeJString(name);
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+ static_cast<enumerator_t*>(baton)
+ ->m_sections.push_back(jobject(jname));
+ return true;
+ }
+ std::vector<jobject> m_sections;
+ } enumerator;
+
+ SVN::Pool requestPool;
+ svn_config_enumerate_sections2(ctx.m_config, enumerator.process, &enumerator,
+ requestPool.getPool());
+ if (JNIUtil::isJavaExceptionThrown())
+ return NULL;
+ return CreateJ::Set(enumerator.m_sections);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_util_ConfigImpl_00024Category_enumerate(
+ JNIEnv* env, jobject jthis, jstring jcategory, jlong jcontext,
+ jstring jsection, jobject jhandler)
+{
+ JNIEntry(ConfigImpl$Category, sections);
+ const ImplContext ctx(env, jthis, jcategory, jcontext, jsection, NULL);
+
+ struct enumerator_t
+ {
+ static svn_boolean_t process(const char* name, const char* value,
+ void* baton, apr_pool_t *pool)
+ {
+ enumerator_t* enmr = static_cast<enumerator_t*>(baton);
+ JNIEnv* const e = enmr->m_env;
+ const jobject jh = enmr->m_jhandler;;
+
+ static jmethodID mid = 0;
+ if (0 == mid)
+ {
+ jclass cls = e->FindClass(JAVAHL_CLASS("/ISVNConfig$Enumerator"));
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+ mid = e->GetMethodID(cls, "option",
+ "(Ljava/lang/String;Ljava/lang/String;)V");
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+ }
+
+ jstring jname = JNIUtil::makeJString(name);
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+ jstring jvalue = JNIUtil::makeJString(value);
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+
+ e->CallVoidMethod(jh, mid, jname, jvalue);
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+
+ e->DeleteLocalRef(jname);
+ e->DeleteLocalRef(jvalue);
+ return true;
+ }
+
+ JNIEnv* m_env;
+ jobject m_jhandler;
+ } enumerator;
+
+ enumerator.m_env = env;
+ enumerator.m_jhandler = jhandler;
+
+ SVN::Pool requestPool;
+ svn_config_enumerate2(ctx.m_config, ctx.m_section.c_str(),
+ enumerator.process, &enumerator,
+ requestPool.getPool());
+}
diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigLib.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigLib.cpp
new file mode 100644
index 0000000..0630098
--- /dev/null
+++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigLib.cpp
@@ -0,0 +1,492 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file org_apache_subversion_javahl_util_ConfigLib.cpp
+ * @brief Implementation of the native methods in the Java class ConfigLib
+ */
+
+#include "../include/org_apache_subversion_javahl_util_ConfigLib.h"
+
+#include <apr_fnmatch.h>
+#include <apr_strings.h>
+
+#include "jniwrapper/jni_list.hpp"
+#include "jniwrapper/jni_stack.hpp"
+
+#include "AuthnCallback.hpp"
+#include "Credential.hpp"
+#include "SubversionException.hpp"
+
+#include "GlobalConfig.h"
+#include "JNIUtil.h"
+#include "JNICriticalSection.h"
+
+#include "svn_auth.h"
+#include "svn_base64.h"
+#include "svn_config.h"
+#include "svn_hash.h"
+#include "svn_x509.h"
+
+#include "svn_private_config.h"
+
+namespace {
+bool g_ignore_native_credentials = false;
+} // anonymous namespace
+
+bool GlobalConfig::useNativeCredentialsStore()
+{
+ JNICriticalSection lock(*JNIUtil::g_configMutex);
+ return !g_ignore_native_credentials;
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_util_ConfigLib_enableNativeCredentialsStore(
+ JNIEnv* jenv, jobject jthis)
+{
+ SVN_JAVAHL_JNI_TRY(ConfigLib, enableNativeCredentialsStore)
+ {
+ JNICriticalSection lock(*JNIUtil::g_configMutex);
+ g_ignore_native_credentials = false;
+ }
+ SVN_JAVAHL_JNI_CATCH;
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_util_ConfigLib_disableNativeCredentialsStore(
+ JNIEnv* jenv, jobject jthis)
+{
+ SVN_JAVAHL_JNI_TRY(ConfigLib, disableNativeCredentialsStore)
+ {
+ JNICriticalSection lock(*JNIUtil::g_configMutex);
+ g_ignore_native_credentials = true;
+ }
+ SVN_JAVAHL_JNI_CATCH;
+}
+
+JNIEXPORT jboolean JNICALL
+Java_org_apache_subversion_javahl_util_ConfigLib_isNativeCredentialsStoreEnabled(
+ JNIEnv* jenv, jobject jthis)
+{
+ SVN_JAVAHL_JNI_TRY(ConfigLib, isNativeCredentialsStoreEnabled)
+ {
+ return jboolean(GlobalConfig::useNativeCredentialsStore());
+ }
+ SVN_JAVAHL_JNI_CATCH;
+ return JNI_FALSE;
+}
+
+
+namespace {
+jobject build_credential(Java::Env env, apr_hash_t* cred,
+ const char* cred_kind, const char* realm,
+ apr_pool_t* scratch_pool)
+{
+ svn_string_t* entry = static_cast<svn_string_t*>(
+ svn_hash_gets(cred, SVN_CONFIG_REALMSTRING_KEY));
+ if (!entry || !realm || 0 != strcmp(realm, entry->data))
+ {
+ JavaHL::SubversionException(env).throw_java_exception(
+ apr_psprintf(scratch_pool,
+ "Unexpected realm; got: [%s], expected: [%s]",
+ (entry ? entry->data : "(null)"),
+ (realm ? realm : "(null)")));
+ }
+
+ entry = static_cast<svn_string_t*>(
+ svn_hash_gets(cred, SVN_CONFIG_AUTHN_PASSTYPE_KEY));
+
+ const char* store = (entry ? entry->data : NULL);
+ const char* username = NULL;
+ const char* password = NULL;
+ jobject info = NULL;
+ jobject failures = NULL;
+ const char* passphrase = NULL;
+
+ if (0 == strcmp(cred_kind, SVN_AUTH_CRED_USERNAME))
+ {
+ entry = static_cast<svn_string_t*>(
+ svn_hash_gets(cred, SVN_CONFIG_AUTHN_USERNAME_KEY));
+ if (entry)
+ username = entry->data;
+ }
+ else if (0 == strcmp(cred_kind, SVN_AUTH_CRED_SIMPLE))
+ {
+ entry = static_cast<svn_string_t*>(
+ svn_hash_gets(cred, SVN_CONFIG_AUTHN_USERNAME_KEY));
+ if (entry)
+ username = entry->data;
+ entry = static_cast<svn_string_t*>(
+ svn_hash_gets(cred, SVN_CONFIG_AUTHN_PASSWORD_KEY));
+ if (entry)
+ password = entry->data;
+ }
+ else if (0 == strcmp(cred_kind, SVN_AUTH_CRED_SSL_SERVER_TRUST))
+ {
+ entry = static_cast<svn_string_t*>(
+ svn_hash_gets(cred, SVN_CONFIG_AUTHN_ASCII_CERT_KEY));
+ const char* ascii_cert = (entry ? entry->data : NULL);
+
+ entry = static_cast<svn_string_t*>(
+ svn_hash_gets(cred, SVN_CONFIG_AUTHN_FAILURES_KEY));
+ jint failflags = (!entry ? 0 : jint(apr_atoi64(entry->data)));
+
+ info = JavaHL::AuthnCallback::SSLServerCertInfo(env, ascii_cert).get();
+ failures = JavaHL::AuthnCallback
+ ::SSLServerCertFailures(env, failflags).get();
+ }
+ else if (0 == strcmp(cred_kind, SVN_AUTH_CRED_SSL_CLIENT_CERT_PW))
+ {
+ entry = static_cast<svn_string_t*>(
+ svn_hash_gets(cred, SVN_CONFIG_AUTHN_PASSPHRASE_KEY));
+ if (entry)
+ passphrase = entry->data;
+ }
+ else
+ {
+ JavaHL::SubversionException(env).throw_java_exception(
+ apr_psprintf(scratch_pool,
+ "Invalid credential type: [%s]",
+ cred_kind));
+ }
+
+ return JavaHL::Credential(
+ env,
+ JavaHL::Credential::Kind(env, Java::String(env, cred_kind)).get(),
+ Java::String(env, realm), Java::String(env, store),
+ Java::String(env, username), Java::String(env, password),
+ info, failures, Java::String(env, passphrase)).get();
+}
+
+class WalkCredentialsCallback
+{
+public:
+ static svn_error_t* walk_func(svn_boolean_t *delete_cred,
+ void *walk_baton,
+ const char *cred_kind,
+ const char *realmstring,
+ apr_hash_t *cred,
+ apr_pool_t *scratch_pool)
+ {
+ WalkCredentialsCallback& self =
+ *static_cast<WalkCredentialsCallback*>(walk_baton);
+ return self(delete_cred, cred_kind, realmstring, cred, scratch_pool);
+ }
+
+ virtual svn_error_t* operator()(svn_boolean_t *delete_cred,
+ const char *cred_kind,
+ const char *realmstring,
+ apr_hash_t *cred_hash,
+ apr_pool_t *scratch_pool) = 0;
+};
+
+class SimpleSearchCallback : public WalkCredentialsCallback
+{
+ const ::Java::Env m_env;
+ const char* const m_cred_kind;
+ const char* const m_realm;
+ const bool m_delete_when_found;
+ jobject m_credential;
+
+public:
+ explicit SimpleSearchCallback(::Java::Env env,
+ const char* cred_kind, const char* realm,
+ bool delete_when_found)
+ : m_env(env),
+ m_cred_kind(cred_kind),
+ m_realm(realm),
+ m_delete_when_found(delete_when_found),
+ m_credential(NULL)
+ {}
+
+ jobject credential() const { return m_credential; }
+
+ virtual svn_error_t* operator()(svn_boolean_t *delete_cred,
+ const char *cred_kind,
+ const char *realmstring,
+ apr_hash_t *cred_hash,
+ apr_pool_t *scratch_pool)
+ {
+ if (0 == strcmp(cred_kind, m_cred_kind)
+ && 0 == strcmp(realmstring, m_realm))
+ {
+ m_credential = build_credential(m_env, cred_hash,
+ cred_kind, realmstring,
+ scratch_pool);
+ *delete_cred = m_delete_when_found;
+ return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, "");
+ }
+
+ *delete_cred = false;
+ return SVN_NO_ERROR;
+ }
+};
+} // anonymous namespace
+
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_util_ConfigLib_nativeGetCredential(
+ JNIEnv* jenv, jobject jthis,
+ jstring jconfig_dir, jstring jcred_kind, jstring jrealm)
+{
+ SVN_JAVAHL_JNI_TRY(ConfigLib, nativeGetCredential)
+ {
+ if (!GlobalConfig::useNativeCredentialsStore())
+ return NULL;
+
+ const Java::Env env(jenv);
+ const Java::String config_dir(env, jconfig_dir);
+ const Java::String cred_kind(env, jcred_kind);
+ const Java::String realm(env, jrealm);
+
+ // Using a "global" request pool since we don't keep a context
+ // with its own pool around for these functions.
+ SVN::Pool pool;
+
+ SimpleSearchCallback cb(env,
+ cred_kind.strdup(pool.getPool()),
+ realm.strdup(pool.getPool()),
+ false);
+
+ SVN_JAVAHL_CHECK(env,
+ svn_config_walk_auth_data(
+ Java::String::Contents(config_dir).c_str(),
+ cb.walk_func, &cb, pool.getPool()));
+ return cb.credential();
+ }
+ SVN_JAVAHL_JNI_CATCH;
+ return NULL;
+}
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_util_ConfigLib_nativeRemoveCredential(
+ JNIEnv* jenv, jobject jthis,
+ jstring jconfig_dir, jstring jcred_kind, jstring jrealm)
+{
+ SVN_JAVAHL_JNI_TRY(ConfigLib, nativeRemoveCredential)
+ {
+ if (!GlobalConfig::useNativeCredentialsStore())
+ return NULL;
+
+ const Java::Env env(jenv);
+ const Java::String config_dir(env, jconfig_dir);
+ const Java::String cred_kind(env, jcred_kind);
+ const Java::String realm(env, jrealm);
+
+ // Using a "global" request pool since we don't keep a context
+ // with its own pool around for these functions.
+ SVN::Pool pool;
+
+ SimpleSearchCallback cb(env,
+ cred_kind.strdup(pool.getPool()),
+ realm.strdup(pool.getPool()),
+ true);
+
+ SVN_JAVAHL_CHECK(env,
+ svn_config_walk_auth_data(
+ Java::String::Contents(config_dir).c_str(),
+ cb.walk_func, &cb, pool.getPool()));
+ return cb.credential();
+ }
+ SVN_JAVAHL_JNI_CATCH;
+ return NULL;
+}
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_util_ConfigLib_nativeSearchCredentials(
+ JNIEnv* jenv, jobject jthis,
+ jstring jconfig_dir, jstring jcred_kind,
+ jstring jrealm_pattern, jstring jusername_pattern,
+ jstring jhostname_pattern, jstring jtext_pattern)
+{
+ SVN_JAVAHL_JNI_TRY(ConfigLib, iterateCredentials)
+ {
+ if (!GlobalConfig::useNativeCredentialsStore())
+ return NULL;
+
+ const Java::Env env(jenv);
+ const Java::String config_dir(env, jconfig_dir);
+ const Java::String cred_kind(env, jcred_kind);
+ const Java::String realm_pattern(env, jrealm_pattern);
+ const Java::String username_pattern(env, jusername_pattern);
+ const Java::String hostname_pattern(env, jhostname_pattern);
+ const Java::String text_pattern(env, jtext_pattern);
+
+ // Using a "global" request pool since we don't keep a context
+ // with its own pool around for these functions.
+ SVN::Pool pool;
+
+ class Callback : public WalkCredentialsCallback
+ {
+ const char* const m_cred_kind;
+ const char* const m_realm_pattern;
+ const char* const m_username_pattern;
+ const char* const m_hostname_pattern;
+ const char* const m_text_pattern;
+
+ const ::Java::Env m_env;
+ ::Java::List<JavaHL::Credential> m_credentials;
+
+ bool match_array(const char* pattern,
+ const apr_array_header_t* hostnames)
+ {
+ for (int i = 0; i < hostnames->nelts; ++i)
+ {
+ const char* const hostname =
+ APR_ARRAY_IDX(hostnames, i, const char*);
+ if (!apr_fnmatch(pattern, hostname, 0))
+ return true;
+ }
+ return false;
+ }
+
+ public:
+ explicit Callback(::Java::Env ctor_env,
+ const char* ctor_cred_kind,
+ const char* ctor_realm_pattern,
+ const char* ctor_username_pattern,
+ const char* ctor_hostname_pattern,
+ const char* ctor_text_pattern)
+ : m_cred_kind(ctor_cred_kind),
+ m_realm_pattern(ctor_realm_pattern),
+ m_username_pattern(ctor_username_pattern),
+ m_hostname_pattern(ctor_hostname_pattern),
+ m_text_pattern(ctor_text_pattern),
+ m_env(ctor_env),
+ m_credentials(ctor_env)
+ {}
+
+ jobject credentials() const
+ {
+ if (m_credentials.is_empty())
+ return NULL;
+ return m_credentials.get();
+ }
+
+ virtual svn_error_t* operator()(svn_boolean_t *delete_cred,
+ const char *cb_cred_kind,
+ const char *cb_realmstring,
+ apr_hash_t *cb_cred_hash,
+ apr_pool_t *cb_scratch_pool)
+ {
+ *delete_cred = false;
+ if (m_cred_kind && 0 != strcmp(cb_cred_kind, m_cred_kind))
+ return SVN_NO_ERROR;
+
+ svn_string_t* entry = static_cast<svn_string_t*>(
+ svn_hash_gets(cb_cred_hash, SVN_CONFIG_AUTHN_USERNAME_KEY));
+ const char* const username = (entry ? entry->data : NULL);
+
+ entry = static_cast<svn_string_t*>(
+ svn_hash_gets(cb_cred_hash, SVN_CONFIG_AUTHN_PASSTYPE_KEY));
+ const char* const store = (entry ? entry->data : NULL);
+
+ const svn_string_t* ascii_cert = static_cast<svn_string_t*>(
+ svn_hash_gets(cb_cred_hash, SVN_CONFIG_AUTHN_ASCII_CERT_KEY));
+
+ /* Parsed certificate data. */
+ const char* subject = NULL;
+ const char* issuer = NULL;
+ const char* fingerprint = NULL;
+ const apr_array_header_t* hostnames = NULL;
+
+ if (ascii_cert)
+ {
+ const svn_string_t* const der =
+ svn_base64_decode_string(ascii_cert, cb_scratch_pool);
+ svn_x509_certinfo_t* certinfo;
+ svn_error_t* err = svn_x509_parse_cert(
+ &certinfo, der->data, der->len,
+ cb_scratch_pool, cb_scratch_pool);
+ if (err)
+ {
+ // Ignore credentials that can't be parsed.
+ svn_error_clear(err);
+ return SVN_NO_ERROR;
+ }
+ else
+ {
+ subject = svn_x509_certinfo_get_subject(
+ certinfo, cb_scratch_pool);
+ issuer = svn_x509_certinfo_get_issuer(
+ certinfo, cb_scratch_pool);
+ fingerprint = svn_checksum_to_cstring_display(
+ svn_x509_certinfo_get_digest(certinfo),
+ cb_scratch_pool);
+ hostnames = svn_x509_certinfo_get_hostnames(certinfo);
+ }
+ }
+
+ bool match = (m_realm_pattern
+ && !apr_fnmatch(m_realm_pattern, cb_realmstring, 0));
+
+ if (!match && m_username_pattern)
+ match = (match
+ || (username
+ && !apr_fnmatch(m_username_pattern, username, 0)));
+
+ if (!match && m_hostname_pattern)
+ match = (match
+ || (hostnames
+ && match_array(m_hostname_pattern, hostnames)));
+
+ if (!match && m_text_pattern)
+ match = (match
+ || (username
+ && !apr_fnmatch(m_text_pattern, username, 0))
+ || (store
+ && !apr_fnmatch(m_text_pattern, store, 0))
+ || (subject
+ && !apr_fnmatch(m_text_pattern, subject, 0))
+ || (issuer
+ && !apr_fnmatch(m_text_pattern, issuer, 0))
+ || (fingerprint
+ && !apr_fnmatch(m_text_pattern, fingerprint, 0))
+ || (hostnames
+ && match_array(m_text_pattern, hostnames)));
+
+ if (match)
+ m_credentials.add(
+ ::JavaHL::Credential(m_env,
+ build_credential(m_env,
+ cb_cred_hash,
+ cb_cred_kind,
+ cb_realmstring,
+ cb_scratch_pool)));
+
+ return SVN_NO_ERROR;
+ }
+ } cb(env,
+ cred_kind.strdup(pool.getPool()),
+ realm_pattern.strdup(pool.getPool()),
+ username_pattern.strdup(pool.getPool()),
+ hostname_pattern.strdup(pool.getPool()),
+ text_pattern.strdup(pool.getPool()));
+
+ SVN_JAVAHL_CHECK(env,
+ svn_config_walk_auth_data(
+ Java::String::Contents(config_dir).c_str(),
+ cb.walk_func, &cb, pool.getPool()));
+ return cb.credentials();
+ }
+ SVN_JAVAHL_JNI_CATCH;
+ return NULL;
+}
diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_DiffLib.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_DiffLib.cpp
new file mode 100644
index 0000000..c6d3d6e
--- /dev/null
+++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_DiffLib.cpp
@@ -0,0 +1,206 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file org_apache_subversion_javahl_util_DiffLib.cpp
+ * @brief Implementation of the native methods in the Java class DiffLib
+ */
+
+#include "../include/org_apache_subversion_javahl_util_DiffLib.h"
+
+#include "JNIStackElement.h"
+#include "JNIStringHolder.h"
+#include "JNIUtil.h"
+#include "OutputStream.h"
+#include "Path.h"
+#include "Pool.h"
+
+#include "svn_diff.h"
+
+JNIEXPORT jboolean JNICALL
+Java_org_apache_subversion_javahl_util_DiffLib_nativeFileDiff(
+ JNIEnv* env, jobject jthis,
+ jstring joriginal_file,
+ jstring jmodified_file,
+
+ jint jignore_space_ordinal,
+ jboolean jignore_eol_style,
+ jboolean jshow_c_function,
+ jint jcontext_size,
+
+ jstring joriginal_header,
+ jstring jmodified_header,
+ jstring jheader_encoding,
+ jstring jrelative_to_dir,
+
+ jobject jresult_stream)
+{
+ JNIEntry(DiffLib, nativeFileDiff);
+
+ // Using a "global" request pool since we don't keep a context with
+ // its own pool around for these functions.
+ SVN::Pool pool;
+
+ Path original(joriginal_file, pool);
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+ SVN_JNI_ERR(original.error_occurred(), false);
+
+ Path modified(jmodified_file, pool);
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+ SVN_JNI_ERR(modified.error_occurred(), false);
+
+ svn_diff_t* diff;
+ svn_diff_file_options_t* diff_options =
+ svn_diff_file_options_create(pool.getPool());
+ diff_options->ignore_space =
+ svn_diff_file_ignore_space_t(jignore_space_ordinal);
+ diff_options->ignore_eol_style = svn_boolean_t(jignore_eol_style);
+ diff_options->show_c_function = svn_boolean_t(jshow_c_function);
+ SVN_JNI_ERR(svn_diff_file_diff_2(&diff,
+ original.c_str(),
+ modified.c_str(),
+ diff_options,
+ pool.getPool()),
+ false);
+
+ const jboolean diffs = svn_diff_contains_diffs(diff);
+
+ JNIStringHolder original_header(joriginal_header);
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+
+ JNIStringHolder modified_header(jmodified_header);
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+
+ JNIStringHolder header_encoding(jheader_encoding);
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+
+ JNIStringHolder relative_to_dir(jrelative_to_dir);
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+
+ OutputStream result_stream(jresult_stream);
+
+ SVN_JNI_ERR(svn_diff_file_output_unified4(
+ result_stream.getStream(pool), diff,
+ original.c_str(), modified.c_str(),
+ original_header.c_str(), modified_header.c_str(),
+ header_encoding.c_str(), relative_to_dir.c_str(),
+ diff_options->show_c_function, int(jcontext_size),
+ NULL, NULL, pool.getPool()),
+ false);
+
+ return diffs;
+}
+
+JNIEXPORT jboolean JNICALL
+Java_org_apache_subversion_javahl_util_DiffLib_nativeFileMerge(
+ JNIEnv* env, jobject jthis,
+ jstring joriginal_file,
+ jstring jmodified_file,
+ jstring jlatest_file,
+
+ jint jignore_space_ordinal,
+ jboolean jignore_eol_style,
+ jboolean jshow_c_function,
+
+ jstring jconflict_original,
+ jstring jconflict_modified,
+ jstring jconflict_latest,
+ jstring jconflict_separator,
+ jint jconflict_style_ordinal,
+
+ jobject jresult_stream)
+{
+ JNIEntry(DiffLib, nativeFileMerge);
+
+ // Using a "global" request pool since we don't keep a context with
+ // its own pool around for these functions.
+ SVN::Pool pool;
+
+ Path original(joriginal_file, pool);
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+ SVN_JNI_ERR(original.error_occurred(), false);
+
+ Path modified(jmodified_file, pool);
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+ SVN_JNI_ERR(modified.error_occurred(), false);
+
+ Path latest(jlatest_file, pool);
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+ SVN_JNI_ERR(latest.error_occurred(), false);
+
+ svn_diff_t* diff;
+ svn_diff_file_options_t* diff_options =
+ svn_diff_file_options_create(pool.getPool());
+ diff_options->ignore_space =
+ svn_diff_file_ignore_space_t(jignore_space_ordinal);
+ diff_options->ignore_eol_style = svn_boolean_t(jignore_eol_style);
+ diff_options->show_c_function = svn_boolean_t(jshow_c_function);
+ SVN_JNI_ERR(svn_diff_file_diff3_2(&diff,
+ original.c_str(),
+ modified.c_str(),
+ latest.c_str(),
+ diff_options,
+ pool.getPool()),
+ false);
+
+ const jboolean conflicts = svn_diff_contains_conflicts(diff);
+
+ JNIStringHolder conflict_original(jconflict_original);
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+
+ JNIStringHolder conflict_modified(jconflict_modified);
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+
+ JNIStringHolder conflict_latest(jconflict_latest);
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+
+ JNIStringHolder conflict_separator(jconflict_separator);
+ if (JNIUtil::isJavaExceptionThrown())
+ return false;
+
+ OutputStream result_stream(jresult_stream);
+
+ SVN_JNI_ERR(svn_diff_file_output_merge3(
+ result_stream.getStream(pool), diff,
+ original.c_str(), modified.c_str(), latest.c_str(),
+ conflict_original.c_str(),
+ conflict_modified.c_str(),
+ conflict_latest.c_str(),
+ conflict_separator.c_str(),
+ svn_diff_conflict_display_style_t(jconflict_style_ordinal),
+ NULL, NULL,
+ pool.getPool()),
+ false);
+
+ return conflicts;
+}
diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_PropLib.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_PropLib.cpp
new file mode 100644
index 0000000..3456dea
--- /dev/null
+++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_PropLib.cpp
@@ -0,0 +1,381 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file org_apache_subversion_javahl_util_PropLib.cpp
+ * @brief Implementation of the native methods in the Java class PropLib
+ */
+
+#include <iostream>
+#include <sstream>
+
+#include "../include/org_apache_subversion_javahl_util_PropLib.h"
+
+#include "jniwrapper/jni_stack.hpp"
+#include "jniwrapper/jni_array.hpp"
+#include "jniwrapper/jni_list.hpp"
+#include "jniwrapper/jni_string.hpp"
+#include "jniwrapper/jni_io_stream.hpp"
+#include "ExternalItem.hpp"
+#include "SubversionException.hpp"
+
+#include "EnumMapper.h"
+#include "Pool.h"
+
+#include "svn_props.h"
+#include "svn_time.h"
+#include "svn_wc.h"
+
+#include "private/svn_wc_private.h"
+#include "svn_private_config.h"
+
+
+namespace {
+class PropGetter
+{
+public:
+ PropGetter(const char* mime_type, svn_stream_t* contents)
+ : m_mime_type(mime_type),
+ m_contents(contents)
+ {}
+
+ static svn_error_t* callback(const svn_string_t** mime_type,
+ svn_stream_t* stream, void* baton,
+ apr_pool_t* pool)
+ {
+ PropGetter* self = static_cast<PropGetter*>(baton);
+ if (mime_type)
+ {
+ if (self->m_mime_type)
+ *mime_type = svn_string_create(self->m_mime_type, pool);
+ else
+ *mime_type = svn_string_create_empty(pool);
+ }
+
+ if (stream && self->m_contents)
+ {
+ SVN_ERR(svn_stream_copy3(self->m_contents,
+ svn_stream_disown(stream, pool),
+ NULL, NULL, pool));
+ }
+
+ return SVN_NO_ERROR;
+ }
+
+private:
+ const char* m_mime_type;
+ svn_stream_t* m_contents;
+};
+
+struct FormatRevision
+{
+ explicit FormatRevision(const svn_opt_revision_t* const& revarg,
+ const SVN::Pool& poolarg)
+ : rev(revarg), pool(poolarg)
+ {}
+
+ const svn_opt_revision_t* const& rev;
+ const SVN::Pool& pool;
+};
+
+std::ostream& operator<<(std::ostream& os, const FormatRevision& pr)
+{
+ switch (pr.rev->kind)
+ {
+ case svn_opt_revision_number:
+ os << pr.rev->value.number;
+ break;
+ case svn_opt_revision_date:
+ os << '{'
+ << svn_time_to_cstring(pr.rev->value.date, pr.pool.getPool())
+ << '}';
+ break;
+ default:
+ throw std::logic_error(
+ _("Invalid revision tag; must be a number or a date"));
+ }
+ return os;
+}
+
+bool operator==(const svn_opt_revision_t& a,
+ const svn_opt_revision_t& b)
+{
+ if (a.kind != b.kind)
+ return false;
+ if (a.kind == svn_opt_revision_number
+ && a.value.number != b.value.number)
+ return false;
+ if (a.kind == svn_opt_revision_date
+ && a.value.date != b.value.date)
+ return false;
+ return true;
+}
+
+inline bool operator!=(const svn_opt_revision_t& a,
+ const svn_opt_revision_t& b)
+{
+ 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
+
+
+JNIEXPORT jbyteArray JNICALL
+Java_org_apache_subversion_javahl_util_PropLib_checkNodeProp(
+ JNIEnv* jenv, jobject jthis,
+ jstring jname, jbyteArray jvalue, jstring jpath, jobject jkind,
+ jstring jmime_type, jobject jfile_contents,
+ jboolean jskip_some_checks)
+{
+ SVN_JAVAHL_JNI_TRY(PropLib, checkLocalProp)
+ {
+ const Java::Env env(jenv);
+
+ const svn_node_kind_t kind = EnumMapper::toNodeKind(jkind);
+ SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env);
+
+ const Java::String name_str(env, jname);
+ const Java::ByteArray value(env, jvalue);
+ const Java::String path_str(env, jpath);
+ const Java::String mime_type_str(env, jmime_type);
+ Java::InputStream file_contents(env, jfile_contents);
+
+ // Using a "global" request pool since we don't keep a context
+ // with its own pool around for these functions.
+ SVN::Pool pool;
+
+ const Java::String::Contents name(name_str);
+ const Java::String::Contents path(path_str);
+ const Java::String::Contents mime_type(mime_type_str);
+ PropGetter getter(mime_type.c_str(), file_contents.get_stream(pool));
+
+ const svn_string_t* canonval;
+ SVN_JAVAHL_CHECK(env,
+ svn_wc_canonicalize_svn_prop(
+ &canonval, name.c_str(),
+ Java::ByteArray::Contents(value).get_string(pool),
+ path.c_str(), kind,
+ svn_boolean_t(jskip_some_checks),
+ PropGetter::callback, &getter,
+ pool.getPool()));
+ return Java::ByteArray(env, canonval->data, jint(canonval->len)).get();
+ }
+ SVN_JAVAHL_JNI_CATCH;
+ return NULL;
+}
+
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_util_PropLib_parseExternals(
+ JNIEnv* jenv, jobject jthis,
+ jbyteArray jdescription, jstring jparent_dir, jboolean jcanonicalize_url)
+{
+ SVN_JAVAHL_JNI_TRY(PropLib, parseExternals)
+ {
+ const Java::Env env(jenv);
+
+ const Java::ByteArray description(env, jdescription);
+ const Java::String parent_dir(env, jparent_dir);
+
+ // Using a "global" request pool since we don't keep a context
+ // with its own pool around for these functions.
+ SVN::Pool pool;
+
+ apr_array_header_t* externals;
+ {
+ // There is no guarantee that the description contents are
+ // null-terminated. Copy them to an svn_string_t to make sure
+ // that they are.
+ svn_string_t* const description_contents =
+ Java::ByteArray::Contents(description).get_string(pool);
+
+ SVN_JAVAHL_CHECK(env,
+ svn_wc_parse_externals_description3(
+ &externals,
+ Java::String::Contents(parent_dir).c_str(),
+ description_contents->data,
+ svn_boolean_t(jcanonicalize_url),
+ pool.getPool()));
+ }
+
+ Java::List<JavaHL::ExternalItem> items(env, externals->nelts);
+ for (jint i = 0; i < externals->nelts; ++i)
+ {
+ // References to the newly created external items are stored
+ // in the list, so make sure the local reference in this
+ // frame get cleared on each iteration.
+ Java::LocalFrame frame;
+
+ const svn_wc_external_item2_t* const item =
+ APR_ARRAY_IDX(externals, i, svn_wc_external_item2_t*);
+ items.add(JavaHL::ExternalItem(env,
+ item->target_dir,
+ item->url,
+ &item->revision,
+ &item->peg_revision));
+ }
+ return items.get();
+ }
+ SVN_JAVAHL_JNI_CATCH;
+ return NULL;
+}
+
+
+JNIEXPORT jbyteArray JNICALL
+Java_org_apache_subversion_javahl_util_PropLib_unparseExternals(
+ JNIEnv* jenv, jobject jthis,
+ jobject jitems, jstring jparent_dir, jboolean jold_format)
+{
+ SVN_JAVAHL_JNI_TRY(PropLib, unparseExternals)
+ {
+ const Java::Env env(jenv);
+
+ const Java::ImmutableList<JavaHL::ExternalItem> items(env, jitems);
+ const Java::String parent_dir(env, jparent_dir);
+
+ // Using a "global" iteration pool since we don't keep a context
+ // with its own pool around for these functions.
+ SVN::Pool iterpool;
+
+ std::ostringstream buffer;
+ 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.
+ SVN_JAVAHL_CHECK(env,
+ svn_wc_parse_externals_description3(
+ NULL,
+ Java::String::Contents(parent_dir).c_str(),
+ description.c_str(),
+ false, iterpool.getPool()));
+ return Java::ByteArray(env, description).get();
+ }
+ SVN_JAVAHL_JNI_CATCH;
+ return NULL;
+}
+
+
+JNIEXPORT jstring JNICALL
+Java_org_apache_subversion_javahl_util_PropLib_resolveExternalsUrl(
+ JNIEnv* jenv, jobject jthis,
+ jobject jitem, jstring jrepos_root_url, jstring jparent_dir_url)
+{
+ SVN_JAVAHL_JNI_TRY(PropLib, unparseExternals)
+ {
+ const Java::Env env(jenv);
+
+ const Java::String repos_root_url(env, jrepos_root_url);
+ const Java::String parent_dir_url(env, jparent_dir_url);
+ const JavaHL::ExternalItem item(env, jitem);
+
+ // Using a "global" request pool since we don't keep a context
+ // with its own pool around for these functions.
+ SVN::Pool pool;
+
+ const char* resolved_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(),
+ Java::String::Contents(parent_dir_url).c_str(),
+ pool.getPool(), pool.getPool()));
+ return Java::String(env, resolved_url).get();
+ }
+ SVN_JAVAHL_JNI_CATCH;
+ return NULL;
+}
diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_SubstLib.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_SubstLib.cpp
new file mode 100644
index 0000000..ce507e5
--- /dev/null
+++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_SubstLib.cpp
@@ -0,0 +1,219 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file org_apache_subversion_javahl_util_SubstLib.cpp
+ * @brief Implementation of the native methods in the Java class SubstLib
+ */
+
+#include <memory>
+
+#include "../include/org_apache_subversion_javahl_util_SubstLib.h"
+
+#include "jniwrapper/jni_stack.hpp"
+#include "jniwrapper/jni_array.hpp"
+#include "jniwrapper/jni_string.hpp"
+#include "jniwrapper/jni_string_map.hpp"
+#include "jniwrapper/jni_io_stream.hpp"
+
+#include "JNIUtil.h"
+#include "NativeStream.hpp"
+#include "Utility.hpp"
+
+#include <apr_hash.h>
+
+#include "svn_subst.h"
+
+#include "svn_private_config.h"
+
+
+namespace {
+apr_hash_t*
+build_keywords_common(Java::Env env, const SVN::Pool& pool,
+ jbyteArray jkeywords_value, jlong jrevision,
+ jstring jurl, jstring jrepos_root_url,
+ jobject jdate, jstring jauthor)
+{
+ const Java::ByteArray keywords_value(env, jkeywords_value);
+ const Java::String url(env, jurl);
+ const Java::String repos_root_url(env, jrepos_root_url);
+ const Java::String author(env, jauthor);
+
+ const Java::ByteArray::Contents keywords_contents(keywords_value);
+ svn_string_t* keywords_string = keywords_contents.get_string(pool);
+ const char* revision = (jrevision < 0 ? NULL
+ : apr_psprintf(pool.getPool(),
+ "%" APR_UINT64_T_FMT,
+ apr_uint64_t(jrevision)));
+ const Java::String::Contents url_contents(url);
+ const Java::String::Contents root_url_contents(repos_root_url);
+ const Java::String::Contents author_contents(author);
+
+ apr_hash_t* kw = NULL;
+ SVN_JAVAHL_CHECK(env,
+ svn_subst_build_keywords3(
+ &kw,
+ keywords_string->data,
+ revision,
+ url_contents.c_str(),
+ root_url_contents.c_str(),
+ (jdate ? JNIUtil::getDate(jdate) : 0),
+ author_contents.c_str(),
+ pool.getPool()));
+ return kw;
+}
+
+svn_stream_t*
+translate_stream_common(Java::Env env, const SVN::Pool& pool,
+ svn_stream_t* stream,
+ jbyteArray jeol_marker, jboolean jrepair_eol,
+ jobject jkeywords, jboolean juse_keywords,
+ jboolean jexpand_keywords,
+ jbyteArray jkeywords_value, jlong jrevision,
+ jstring jurl, jstring jrepos_root_url,
+ jobject jdate, jstring jauthor)
+{
+ apr_hash_t* const keywords =
+ (juse_keywords
+ ? JavaHL::Util::make_keyword_hash(env, jkeywords, pool)
+ : build_keywords_common(
+ env, pool, jkeywords_value, jrevision,
+ jurl, jrepos_root_url, jdate, jauthor));
+
+ const Java::ByteArray eol_marker(env, jeol_marker);
+ svn_string_t* const eol_str =
+ Java::ByteArray::Contents(eol_marker).get_string(pool);
+ return svn_subst_stream_translated(stream,
+ eol_str->data,
+ svn_boolean_t(jrepair_eol),
+ keywords,
+ svn_boolean_t(jexpand_keywords),
+ pool.getPool());
+}
+} // anoymous namespace
+
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_util_SubstLib_buildKeywords(
+ JNIEnv* jenv, jobject jthis,
+ jbyteArray jkeywords_value, jlong jrevision,
+ jstring jurl, jstring jrepos_root_url,
+ jobject jdate, jstring jauthor)
+{
+ typedef Java::Map<Java::ByteArray, jbyteArray> ByteArrayMap;
+
+ SVN_JAVAHL_JNI_TRY(SubstLib, buildKeywords)
+ {
+ const Java::Env env(jenv);
+
+ // Using a "global" request pool since we don't keep a context with
+ // its own pool around for these functions.
+ SVN::Pool pool;
+
+ apr_hash_t* const kw = build_keywords_common(
+ env, pool, jkeywords_value, jrevision,
+ jurl, jrepos_root_url, jdate, jauthor);
+
+ ByteArrayMap keywords(env, jint(apr_hash_count(kw)));
+ for (apr_hash_index_t* hi = apr_hash_first(pool.getPool(), kw);
+ hi; hi = apr_hash_next(hi))
+ {
+ const void* rkey;
+ void* rval;
+ apr_hash_this(hi, &rkey, NULL, &rval);
+
+ svn_string_t* const val = static_cast<svn_string_t*>(rval);
+ keywords.put(static_cast<const char*>(rkey),
+ Java::ByteArray(env, val->data, jsize(val->len)));
+ }
+ return keywords.get();
+ }
+ SVN_JAVAHL_JNI_CATCH;
+ return NULL;
+}
+
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_util_SubstLib_translateInputStream(
+ JNIEnv* jenv, jobject jthis,
+ jobject jsource, jbyteArray jeol_marker, jboolean jrepair_eol,
+ jobject jkeywords, jboolean juse_keywords, jboolean jexpand_keywords,
+ jbyteArray jkeywords_value, jlong jrevision,
+ jstring jurl, jstring jrepos_root_url,
+ jobject jdate, jstring jauthor)
+{
+ SVN_JAVAHL_JNI_TRY(SubstLib, translateInputStream)
+ {
+ const Java::Env env(jenv);
+
+ // We'll allocate the stream in the bound object's pool.
+ std::auto_ptr<JavaHL::NativeInputStream>
+ translated(new JavaHL::NativeInputStream());
+ svn_stream_t* source = Java::InputStream::get_global_stream(
+ env, jsource, translated->get_pool());
+
+ translated->set_stream(translate_stream_common(
+ env, translated->get_pool(), source,
+ jeol_marker, jrepair_eol,
+ jkeywords, juse_keywords, jexpand_keywords,
+ jkeywords_value, jrevision,
+ jurl, jrepos_root_url, jdate, jauthor));
+ const jobject jtranslated = translated->create_java_wrapper();
+ translated.release();
+ return jtranslated;
+ }
+ SVN_JAVAHL_JNI_CATCH;
+ return NULL;
+}
+
+
+JNIEXPORT jobject JNICALL
+Java_org_apache_subversion_javahl_util_SubstLib_translateOutputStream(
+ JNIEnv* jenv, jobject jthis,
+ jobject jdestination, jbyteArray jeol_marker, jboolean jrepair_eol,
+ jobject jkeywords, jboolean juse_keywords, jboolean jexpand_keywords,
+ jbyteArray jkeywords_value, jlong jrevision,
+ jstring jurl, jstring jrepos_root_url,
+ jobject jdate, jstring jauthor)
+{
+ SVN_JAVAHL_JNI_TRY(SubstLib, translateInputStream)
+ {
+ const Java::Env env(jenv);
+
+ // We'll allocate the stream in the bound object's pool.
+ std::auto_ptr<JavaHL::NativeOutputStream>
+ translated(new JavaHL::NativeOutputStream());
+ svn_stream_t* destination = Java::OutputStream::get_global_stream(
+ env, jdestination, translated->get_pool());
+
+ translated->set_stream(translate_stream_common(
+ env, translated->get_pool(), destination,
+ jeol_marker, jrepair_eol,
+ jkeywords, juse_keywords, jexpand_keywords,
+ jkeywords_value, jrevision,
+ jurl, jrepos_root_url, jdate, jauthor));
+ const jobject jtranslated = translated->create_java_wrapper();
+ translated.release();
+ return jtranslated;
+ }
+ SVN_JAVAHL_JNI_CATCH;
+ return NULL;
+}
diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_TunnelChannel.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_TunnelChannel.cpp
new file mode 100644
index 0000000..b18f3fc
--- /dev/null
+++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_TunnelChannel.cpp
@@ -0,0 +1,174 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file org_apache_subversion_javahl_util_TunnelChannel.cpp
+ * @brief Implementation of the native methods in the Java classes
+ * TunnelChannel, RequestChannel and ResponseChannel
+ */
+
+#include <string>
+
+#include <apr_file_io.h>
+
+#include "../include/org_apache_subversion_javahl_util_TunnelChannel.h"
+#include "../include/org_apache_subversion_javahl_util_RequestChannel.h"
+#include "../include/org_apache_subversion_javahl_util_ResponseChannel.h"
+
+#include "jniwrapper/jni_exception.hpp"
+#include "jniwrapper/jni_channel.hpp"
+#include "jniwrapper/jni_stack.hpp"
+
+#include "svn_private_config.h"
+
+namespace {
+apr_file_t* get_file_descriptor(Java::Env env, jlong jfd)
+{
+ apr_file_t* fd = reinterpret_cast<apr_file_t*>(jfd);
+ if (!fd)
+ Java::NullPointerException(env).raise("nativeChannel");
+ return fd;
+}
+
+void throw_IOException(Java::Env env, const char* message,
+ apr_status_t status)
+{
+ char buf[1024];
+ std::string msg(message);
+ apr_strerror(status, buf, sizeof(buf) - 1);
+ msg += buf;
+ Java::IOException(env).raise(msg.c_str());
+}
+
+class TunnelReader : public Java::ChannelReader
+{
+public:
+ explicit TunnelReader(Java::Env env, jlong jnative_channel)
+ : m_fd(get_file_descriptor(env, jnative_channel))
+ {}
+
+ virtual jint operator()(Java::Env env, void* buffer, jint length)
+ {
+ if (!length)
+ return 0;
+
+ apr_size_t bytes_read = length;
+ const apr_status_t status = apr_file_read(m_fd, buffer, &bytes_read);
+ if (status && !APR_STATUS_IS_EOF(status))
+ {
+ throw_IOException(
+ env, _("Error reading from native file handle: "),
+ status);
+ return -1;
+ }
+ if (APR_STATUS_IS_EOF(status))
+ return -1;
+ return jint(bytes_read);
+ }
+
+private:
+ apr_file_t* const m_fd;
+};
+
+class TunnelWriter : public Java::ChannelWriter
+{
+public:
+ explicit TunnelWriter(Java::Env env, jlong jnative_channel)
+ : m_fd(get_file_descriptor(env, jnative_channel))
+ {}
+
+ virtual jint operator()(Java::Env env, const void* buffer, jint length)
+ {
+ if (!length)
+ return 0;
+
+ apr_size_t bytes_written;
+ const apr_status_t status =
+ apr_file_write_full(m_fd, buffer, length, &bytes_written);
+ if (status)
+ {
+ throw_IOException(
+ env, _("Error writing to native file handle: "),
+ status);
+ return -1;
+ }
+ return jint(bytes_written);
+ }
+
+private:
+ apr_file_t* const m_fd;
+};
+
+} // anonymous namespace
+
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_util_TunnelChannel_nativeClose(
+ JNIEnv* jenv, jclass jclazz, jlong jnative_channel)
+{
+ SVN_JAVAHL_JNI_TRY_STATIC(TunnelChannel, close)
+ {
+ const Java::Env env(jenv);
+
+ apr_file_t* const fd = get_file_descriptor(env, jnative_channel);
+ if (!fd)
+ return;
+
+ const apr_status_t status = apr_file_close(fd);
+ if (status)
+ throw_IOException(
+ env, _("Error closing native file handle: "),
+ status);
+ }
+ SVN_JAVAHL_JNI_CATCH;
+}
+
+JNIEXPORT jint JNICALL
+Java_org_apache_subversion_javahl_util_RequestChannel_nativeRead(
+ JNIEnv* jenv, jclass jclazz, jlong jnative_channel, jobject jdst_buffer)
+{
+ SVN_JAVAHL_JNI_TRY_STATIC(RequestChannel, read)
+ {
+ const Java::Env env(jenv);
+
+ TunnelReader reader(env, jnative_channel);
+ Java::ReadableByteChannel channel(env, reader);
+ return channel.read(jdst_buffer);
+ }
+ SVN_JAVAHL_JNI_CATCH;
+ return -1;
+}
+
+JNIEXPORT jint JNICALL
+Java_org_apache_subversion_javahl_util_ResponseChannel_nativeWrite(
+ JNIEnv* jenv, jclass jclazz, jlong jnative_channel, jobject jsrc_buffer)
+{
+ SVN_JAVAHL_JNI_TRY_STATIC(ResponseChannel, write)
+ {
+ const Java::Env env(jenv);
+
+ TunnelWriter writer(env, jnative_channel);
+ Java::WritableByteChannel channel(env, writer);
+ return channel.write(jsrc_buffer);
+ }
+ SVN_JAVAHL_JNI_CATCH;
+ return -1;
+}