blob: ac10f9e2589905bfd747673c9b748d7339e43567 [file] [log] [blame]
/**
* @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 InputStream.cpp
* @brief Implementation of the class InputStream
*/
#include "InputStream.h"
#include "JNIUtil.h"
#include "JNIByteArray.h"
/**
* Create an InputStream object.
* @param jthis the Java object to be stored
*/
InputStream::InputStream(jobject jthis)
{
m_jthis = jthis;
}
InputStream::~InputStream()
{
// The m_jthis does not need to be destroyed, because it is the
// passed in parameter to the Java method.
}
/**
* Create a svn_stream_t structure for this object. This will be used
* as an input stream by Subversion.
* @param pool the pool, from which the structure is allocated
* @return the input stream
*/
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_read2(ret, InputStream::read,
NULL /* only partial read support */);
svn_stream_set_close(ret, InputStream::close);
return ret;
}
/**
* Implements svn_read_fn_t to read to data into Subversion.
* @param baton an InputStream object for the callback
* @param buffer the buffer for the read data
* @param len on input the buffer len, on output the number of read bytes
* @return a subversion error or SVN_NO_ERROR
*/
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);
// 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/io/InputStream");
if (JNIUtil::isJavaExceptionThrown())
return SVN_NO_ERROR;
mid = env->GetMethodID(clazz, "read", "([B)I");
if (JNIUtil::isJavaExceptionThrown() || mid == 0)
return SVN_NO_ERROR;
env->DeleteLocalRef(clazz);
}
// Allocate a Java byte array to read the data.
jbyteArray data = JNIUtil::makeJByteArray(buffer, static_cast<int>(*len));
if (JNIUtil::isJavaExceptionThrown())
return SVN_NO_ERROR;
// Read the data.
jint jread = env->CallIntMethod(that->m_jthis, mid, data);
if (JNIUtil::isJavaExceptionThrown())
return SVN_NO_ERROR;
/*
* Convert -1 from InputStream.read that means EOF, 0 which is subversion equivalent
*/
if(jread == -1)
{
jread = 0;
}
// Put the Java byte array into a helper object to retrieve the
// data bytes.
JNIByteArray outdata(data, true);
if (JNIUtil::isJavaExceptionThrown())
return SVN_NO_ERROR;
// Catch when the Java method tells us it read too much data.
if (jread > (jint) *len)
jread = 0;
// In the case of success copy the data back to the Subversion
// buffer.
if (jread > 0)
memcpy(buffer, outdata.getBytes(), jread);
// Copy the number of read bytes back to Subversion.
*len = jread;
return SVN_NO_ERROR;
}
/**
* Implements svn_close_fn_t to close the input stream.
* @param baton an InputStream object for the callback
* @return a subversion error or SVN_NO_ERROR
*/
svn_error_t *InputStream::close(void *baton)
{
JNIEnv *env = JNIUtil::getEnv();
// An object of our class is passed in as the baton
InputStream *that = reinterpret_cast<InputStream*>(baton);
// 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/io/InputStream");
if (JNIUtil::isJavaExceptionThrown())
return SVN_NO_ERROR;
mid = env->GetMethodID(clazz, "close", "()V");
if (JNIUtil::isJavaExceptionThrown() || mid == 0)
return SVN_NO_ERROR;
env->DeleteLocalRef(clazz);
}
// Call the Java object, to close the stream.
env->CallVoidMethod(that->m_jthis, mid);
// We don't need to check for an exception here because we return anyway.
return SVN_NO_ERROR;
}