<commit_before>/*
 *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/modules/audio_device/android/audio_record_jni.h"

#include <android/log.h>

#include "webrtc/base/arraysize.h"
#include "webrtc/base/checks.h"
#include "webrtc/modules/audio_device/android/audio_common.h"

#define TAG "AudioRecordJni"
#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)

namespace webrtc {

// Number of bytes per audio frame.
// Example: 16-bit PCM in mono => 1*(16/8)=2 [bytes/frame]
static const int kBytesPerFrame = kNumChannels * (kBitsPerSample / 8);

// We are unable to obtain exact measurements of the hardware delay on Android.
// Instead, a lower bound (based on measurements) is used.
// TODO(henrika): is it possible to improve this?
static const int kHardwareDelayInMilliseconds = 100;

static JavaVM* g_jvm = NULL;
static jobject g_context = NULL;
static jclass g_audio_record_class = NULL;

void AudioRecordJni::SetAndroidAudioDeviceObjects(void* jvm, void* env,
                                                  void* context) {
  ALOGD("SetAndroidAudioDeviceObjects%s", GetThreadInfo().c_str());

  CHECK(jvm);
  CHECK(env);
  CHECK(context);

  g_jvm = reinterpret_cast<JavaVM*>(jvm);
  JNIEnv* jni = GetEnv(g_jvm);
  CHECK(jni) << "AttachCurrentThread must be called on this tread";

  // Protect context from being deleted during garbage collection.
  g_context = NewGlobalRef(jni, reinterpret_cast<jobject>(context));

  // Load the locally-defined WebRtcAudioRecord class and create a new global
  // reference to it.
  jclass local_class = FindClass(
      jni, "org/webrtc/voiceengine/WebRtcAudioRecord");
  g_audio_record_class = reinterpret_cast<jclass>(
      NewGlobalRef(jni, local_class));

  // Register native methods with the WebRtcAudioRecord class. These methods
  // are declared private native in WebRtcAudioRecord.java.
  JNINativeMethod native_methods[] = {
      {"nativeCacheDirectBufferAddress", "(Ljava/nio/ByteBuffer;J)V",
          reinterpret_cast<void*>(
       &webrtc::AudioRecordJni::CacheDirectBufferAddress)},
      {"nativeDataIsRecorded", "(IJ)V",
          reinterpret_cast<void*>(&webrtc::AudioRecordJni::DataIsRecorded)}};
  jni->RegisterNatives(g_audio_record_class,
                       native_methods, arraysize(native_methods));
  CHECK_EXCEPTION(jni) << "Error during RegisterNatives";
}

void AudioRecordJni::ClearAndroidAudioDeviceObjects() {
  ALOGD("ClearAndroidAudioDeviceObjects%s", GetThreadInfo().c_str());
  JNIEnv* jni = GetEnv(g_jvm);
  CHECK(jni) << "AttachCurrentThread must be called on this tread";
  jni->UnregisterNatives(g_audio_record_class);
  CHECK_EXCEPTION(jni) << "Error during UnregisterNatives";
  DeleteGlobalRef(jni, g_audio_record_class);
  g_audio_record_class = NULL;
  DeleteGlobalRef(jni, g_context);
  g_context = NULL;
  g_jvm = NULL;
}

AudioRecordJni::AudioRecordJni()
    : j_audio_record_(NULL),
      direct_buffer_address_(NULL),
      direct_buffer_capacity_in_bytes_(0),
      frames_per_buffer_(0),
      initialized_(false),
      recording_(false),
      audio_device_buffer_(NULL),
      sample_rate_hz_(0) {
  ALOGD("ctor%s", GetThreadInfo().c_str());
  CHECK(HasDeviceObjects());
  CreateJavaInstance();
  // Detach from this thread since we want to use the checker to verify calls
  // from the Java based audio thread.
  thread_checker_java_.DetachFromThread();
}

AudioRecordJni::~AudioRecordJni() {
  ALOGD("~dtor%s", GetThreadInfo().c_str());
  DCHECK(thread_checker_.CalledOnValidThread());
  Terminate();
  AttachThreadScoped ats(g_jvm);
  JNIEnv* jni = ats.env();
  jni->DeleteGlobalRef(j_audio_record_);
  j_audio_record_ = NULL;
}

int32_t AudioRecordJni::Init() {
  ALOGD("Init%s", GetThreadInfo().c_str());
  DCHECK(thread_checker_.CalledOnValidThread());
  return 0;
}

int32_t AudioRecordJni::Terminate() {
  ALOGD("Terminate%s", GetThreadInfo().c_str());
  DCHECK(thread_checker_.CalledOnValidThread());
  StopRecording();
  return 0;
}

int32_t AudioRecordJni::InitRecording() {
  ALOGD("InitRecording%s", GetThreadInfo().c_str());
  DCHECK(thread_checker_.CalledOnValidThread());
  DCHECK(!initialized_);
  DCHECK(!recording_);
  if (initialized_ || recording_) {
    return -1;
  }
  AttachThreadScoped ats(g_jvm);
  JNIEnv* jni = ats.env();
  jmethodID initRecordingID = GetMethodID(
      jni, g_audio_record_class, "InitRecording", "(I)I");
  jint frames_per_buffer = jni->CallIntMethod(
      j_audio_record_, initRecordingID, sample_rate_hz_);
  CHECK_EXCEPTION(jni);
  if (frames_per_buffer < 0) {
    ALOGE("InitRecording failed!");
    return -1;
  }
  frames_per_buffer_ = frames_per_buffer;
  ALOGD("frames_per_buffer: %d", frames_per_buffer_);
  CHECK_EQ(direct_buffer_capacity_in_bytes_,
           frames_per_buffer_ * kBytesPerFrame);
  initialized_ = true;
  return 0;
}

int32_t AudioRecordJni::StartRecording() {
  ALOGD("StartRecording%s", GetThreadInfo().c_str());
  DCHECK(thread_checker_.CalledOnValidThread());
  DCHECK(initialized_);
  DCHECK(!recording_);
  if (!initialized_ || recording_) {
    return -1;
  }
  AttachThreadScoped ats(g_jvm);
  JNIEnv* jni = ats.env();
  jmethodID startRecordingID = GetMethodID(
      jni, g_audio_record_class, "StartRecording", "()Z");
  jboolean res = jni->CallBooleanMethod(j_audio_record_, startRecordingID);
  CHECK_EXCEPTION(jni);
  if (!res) {
    ALOGE("StartRecording failed!");
    return -1;
  }
  return 0;
}

int32_t AudioRecordJni::StopRecording() {
  ALOGD("StopRecording%s", GetThreadInfo().c_str());
  DCHECK(thread_checker_.CalledOnValidThread());
  if (!initialized_) {
    return 0;
  }
  AttachThreadScoped ats(g_jvm);
  JNIEnv* jni = ats.env();
  jmethodID stopRecordingID = GetMethodID(
      jni, g_audio_record_class, "StopRecording", "()Z");
  jboolean res = jni->CallBooleanMethod(j_audio_record_, stopRecordingID);
  CHECK_EXCEPTION(jni);
  if (!res) {
    ALOGE("StopRecording failed!");
    return -1;
  }
  // If we don't detach here, we will hit a DCHECK in OnDataIsRecorded() next
  // time StartRecording() is called since it will create a new Java thread.
  thread_checker_java_.DetachFromThread();
  initialized_ = false;
  recording_ = false;
  return 0;

}

int32_t AudioRecordJni::RecordingDelay(uint16_t& delayMS) const {  // NOLINT
  // TODO(henrika): is it possible to improve this estimate?
  delayMS = kHardwareDelayInMilliseconds;
  return 0;
}

void AudioRecordJni::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
  ALOGD("AttachAudioBuffer");
  DCHECK(thread_checker_.CalledOnValidThread());
  audio_device_buffer_ = audioBuffer;
  sample_rate_hz_ = GetNativeSampleRate();
  ALOGD("SetRecordingSampleRate(%d)", sample_rate_hz_);
  audio_device_buffer_->SetRecordingSampleRate(sample_rate_hz_);
  audio_device_buffer_->SetRecordingChannels(kNumChannels);
}

bool AudioRecordJni::BuiltInAECIsAvailable() const {
  ALOGD("BuiltInAECIsAvailable%s", GetThreadInfo().c_str());
  AttachThreadScoped ats(g_jvm);
  JNIEnv* jni = ats.env();
  jmethodID builtInAECIsAvailable = jni->GetStaticMethodID(
      g_audio_record_class, "BuiltInAECIsAvailable", "()Z");
  CHECK_EXCEPTION(jni);
  CHECK(builtInAECIsAvailable);
  jboolean hw_aec = jni->CallStaticBooleanMethod(g_audio_record_class,
                                                 builtInAECIsAvailable);
  CHECK_EXCEPTION(jni);
  return hw_aec;
}

int32_t AudioRecordJni::EnableBuiltInAEC(bool enable) {
  ALOGD("EnableBuiltInAEC%s", GetThreadInfo().c_str());
  DCHECK(thread_checker_.CalledOnValidThread());
  AttachThreadScoped ats(g_jvm);
  JNIEnv* jni = ats.env();
  jmethodID enableBuiltInAEC = GetMethodID(
      jni, g_audio_record_class, "EnableBuiltInAEC", "(Z)Z");
  jboolean res = jni->CallBooleanMethod(
      j_audio_record_, enableBuiltInAEC, enable);
  CHECK_EXCEPTION(jni);
  if (!res) {
    ALOGE("EnableBuiltInAEC failed!");
    return -1;
  }
  return 0;
}

void JNICALL AudioRecordJni::CacheDirectBufferAddress(
    JNIEnv* env, jobject obj, jobject byte_buffer, jlong nativeAudioRecord) {
  webrtc::AudioRecordJni* this_object =
      reinterpret_cast<webrtc::AudioRecordJni*> (nativeAudioRecord);
  this_object->OnCacheDirectBufferAddress(env, byte_buffer);
}

void AudioRecordJni::OnCacheDirectBufferAddress(
    JNIEnv* env, jobject byte_buffer) {
  ALOGD("OnCacheDirectBufferAddress");
  DCHECK(thread_checker_.CalledOnValidThread());
  direct_buffer_address_ =
      env->GetDirectBufferAddress(byte_buffer);
  jlong capacity = env->GetDirectBufferCapacity(byte_buffer);
  ALOGD("direct buffer capacity: %lld", capacity);
  direct_buffer_capacity_in_bytes_ = static_cast<int> (capacity);
}

void JNICALL AudioRecordJni::DataIsRecorded(
  JNIEnv* env, jobject obj, jint length, jlong nativeAudioRecord) {
  webrtc::AudioRecordJni* this_object =
      reinterpret_cast<webrtc::AudioRecordJni*> (nativeAudioRecord  );
  this_object->OnDataIsRecorded(length);
}

// This method is called on a high-priority thread from Java. The name of
// the thread is 'AudioRecordThread'.
void AudioRecordJni::OnDataIsRecorded(int length) {
  DCHECK(thread_checker_java_.CalledOnValidThread());
  audio_device_buffer_->SetRecordedBuffer(direct_buffer_address_,
                                          frames_per_buffer_);
  // TODO(henrika): improve playout delay estimate.
  audio_device_buffer_->SetVQEData(0, kHardwareDelayInMilliseconds, 0);
  audio_device_buffer_->DeliverRecordedData();
}

bool AudioRecordJni::HasDeviceObjects() {
  return (g_jvm && g_context && g_audio_record_class);
}

void AudioRecordJni::CreateJavaInstance() {
  ALOGD("CreateJavaInstance");
  AttachThreadScoped ats(g_jvm);
  JNIEnv* jni = ats.env();
  jmethodID constructorID = GetMethodID(
      jni, g_audio_record_class, "<init>", "(Landroid/content/Context;J)V");
  j_audio_record_ = jni->NewObject(g_audio_record_class,
                                   constructorID,
                                   g_context,
                                   reinterpret_cast<intptr_t>(this));
  CHECK_EXCEPTION(jni) << "Error during NewObject";
  CHECK(j_audio_record_);
  j_audio_record_ = jni->NewGlobalRef(j_audio_record_);
  CHECK_EXCEPTION(jni) << "Error during NewGlobalRef";
  CHECK(j_audio_record_);
}

int AudioRecordJni::GetNativeSampleRate() {
  AttachThreadScoped ats(g_jvm);
  JNIEnv* jni = ats.env();
  jmethodID getNativeSampleRate = GetMethodID(
      jni, g_audio_record_class, "GetNativeSampleRate", "()I");
  jint sample_rate_hz = jni->CallIntMethod(
      j_audio_record_, getNativeSampleRate);
  CHECK_EXCEPTION(jni);
  return sample_rate_hz;
}

}  // namespace webrtc
<commit_msg>Fix problem where Android VoE can not record on multiple channels.<commit_after>/*
 *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/modules/audio_device/android/audio_record_jni.h"

#include <android/log.h>

#include "webrtc/base/arraysize.h"
#include "webrtc/base/checks.h"
#include "webrtc/modules/audio_device/android/audio_common.h"

#define TAG "AudioRecordJni"
#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)

namespace webrtc {

// Number of bytes per audio frame.
// Example: 16-bit PCM in mono => 1*(16/8)=2 [bytes/frame]
static const int kBytesPerFrame = kNumChannels * (kBitsPerSample / 8);

// We are unable to obtain exact measurements of the hardware delay on Android.
// Instead, a lower bound (based on measurements) is used.
// TODO(henrika): is it possible to improve this?
static const int kHardwareDelayInMilliseconds = 100;

static JavaVM* g_jvm = NULL;
static jobject g_context = NULL;
static jclass g_audio_record_class = NULL;

void AudioRecordJni::SetAndroidAudioDeviceObjects(void* jvm, void* env,
                                                  void* context) {
  ALOGD("SetAndroidAudioDeviceObjects%s", GetThreadInfo().c_str());

  CHECK(jvm);
  CHECK(env);
  CHECK(context);

  g_jvm = reinterpret_cast<JavaVM*>(jvm);
  JNIEnv* jni = GetEnv(g_jvm);
  CHECK(jni) << "AttachCurrentThread must be called on this tread";

  // Protect context from being deleted during garbage collection.
  g_context = NewGlobalRef(jni, reinterpret_cast<jobject>(context));

  // Load the locally-defined WebRtcAudioRecord class and create a new global
  // reference to it.
  jclass local_class = FindClass(
      jni, "org/webrtc/voiceengine/WebRtcAudioRecord");
  g_audio_record_class = reinterpret_cast<jclass>(
      NewGlobalRef(jni, local_class));

  // Register native methods with the WebRtcAudioRecord class. These methods
  // are declared private native in WebRtcAudioRecord.java.
  JNINativeMethod native_methods[] = {
      {"nativeCacheDirectBufferAddress", "(Ljava/nio/ByteBuffer;J)V",
          reinterpret_cast<void*>(
       &webrtc::AudioRecordJni::CacheDirectBufferAddress)},
      {"nativeDataIsRecorded", "(IJ)V",
          reinterpret_cast<void*>(&webrtc::AudioRecordJni::DataIsRecorded)}};
  jni->RegisterNatives(g_audio_record_class,
                       native_methods, arraysize(native_methods));
  CHECK_EXCEPTION(jni) << "Error during RegisterNatives";
}

void AudioRecordJni::ClearAndroidAudioDeviceObjects() {
  ALOGD("ClearAndroidAudioDeviceObjects%s", GetThreadInfo().c_str());
  JNIEnv* jni = GetEnv(g_jvm);
  CHECK(jni) << "AttachCurrentThread must be called on this tread";
  jni->UnregisterNatives(g_audio_record_class);
  CHECK_EXCEPTION(jni) << "Error during UnregisterNatives";
  DeleteGlobalRef(jni, g_audio_record_class);
  g_audio_record_class = NULL;
  DeleteGlobalRef(jni, g_context);
  g_context = NULL;
  g_jvm = NULL;
}

AudioRecordJni::AudioRecordJni()
    : j_audio_record_(NULL),
      direct_buffer_address_(NULL),
      direct_buffer_capacity_in_bytes_(0),
      frames_per_buffer_(0),
      initialized_(false),
      recording_(false),
      audio_device_buffer_(NULL),
      sample_rate_hz_(0) {
  ALOGD("ctor%s", GetThreadInfo().c_str());
  CHECK(HasDeviceObjects());
  CreateJavaInstance();
  // Detach from this thread since we want to use the checker to verify calls
  // from the Java based audio thread.
  thread_checker_java_.DetachFromThread();
}

AudioRecordJni::~AudioRecordJni() {
  ALOGD("~dtor%s", GetThreadInfo().c_str());
  DCHECK(thread_checker_.CalledOnValidThread());
  Terminate();
  AttachThreadScoped ats(g_jvm);
  JNIEnv* jni = ats.env();
  jni->DeleteGlobalRef(j_audio_record_);
  j_audio_record_ = NULL;
}

int32_t AudioRecordJni::Init() {
  ALOGD("Init%s", GetThreadInfo().c_str());
  DCHECK(thread_checker_.CalledOnValidThread());
  return 0;
}

int32_t AudioRecordJni::Terminate() {
  ALOGD("Terminate%s", GetThreadInfo().c_str());
  DCHECK(thread_checker_.CalledOnValidThread());
  StopRecording();
  return 0;
}

int32_t AudioRecordJni::InitRecording() {
  ALOGD("InitRecording%s", GetThreadInfo().c_str());
  DCHECK(thread_checker_.CalledOnValidThread());
  DCHECK(!initialized_);
  DCHECK(!recording_);
  if (initialized_ || recording_) {
    return -1;
  }
  AttachThreadScoped ats(g_jvm);
  JNIEnv* jni = ats.env();
  jmethodID initRecordingID = GetMethodID(
      jni, g_audio_record_class, "InitRecording", "(I)I");
  jint frames_per_buffer = jni->CallIntMethod(
      j_audio_record_, initRecordingID, sample_rate_hz_);
  CHECK_EXCEPTION(jni);
  if (frames_per_buffer < 0) {
    ALOGE("InitRecording failed!");
    return -1;
  }
  frames_per_buffer_ = frames_per_buffer;
  ALOGD("frames_per_buffer: %d", frames_per_buffer_);
  CHECK_EQ(direct_buffer_capacity_in_bytes_,
           frames_per_buffer_ * kBytesPerFrame);
  initialized_ = true;
  return 0;
}

int32_t AudioRecordJni::StartRecording() {
  ALOGD("StartRecording%s", GetThreadInfo().c_str());
  DCHECK(thread_checker_.CalledOnValidThread());
  DCHECK(initialized_);
  DCHECK(!recording_);
  if (!initialized_ || recording_) {
    return -1;
  }
  AttachThreadScoped ats(g_jvm);
  JNIEnv* jni = ats.env();
  jmethodID startRecordingID = GetMethodID(
      jni, g_audio_record_class, "StartRecording", "()Z");
  jboolean res = jni->CallBooleanMethod(j_audio_record_, startRecordingID);
  CHECK_EXCEPTION(jni);
  if (!res) {
    ALOGE("StartRecording failed!");
    return -1;
  }
  recording_ = true;
  return 0;
}

int32_t AudioRecordJni::StopRecording() {
  ALOGD("StopRecording%s", GetThreadInfo().c_str());
  DCHECK(thread_checker_.CalledOnValidThread());
  if (!initialized_) {
    return 0;
  }
  AttachThreadScoped ats(g_jvm);
  JNIEnv* jni = ats.env();
  jmethodID stopRecordingID = GetMethodID(
      jni, g_audio_record_class, "StopRecording", "()Z");
  jboolean res = jni->CallBooleanMethod(j_audio_record_, stopRecordingID);
  CHECK_EXCEPTION(jni);
  if (!res) {
    ALOGE("StopRecording failed!");
    return -1;
  }
  // If we don't detach here, we will hit a DCHECK in OnDataIsRecorded() next
  // time StartRecording() is called since it will create a new Java thread.
  thread_checker_java_.DetachFromThread();
  initialized_ = false;
  recording_ = false;
  return 0;

}

int32_t AudioRecordJni::RecordingDelay(uint16_t& delayMS) const {  // NOLINT
  // TODO(henrika): is it possible to improve this estimate?
  delayMS = kHardwareDelayInMilliseconds;
  return 0;
}

void AudioRecordJni::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
  ALOGD("AttachAudioBuffer");
  DCHECK(thread_checker_.CalledOnValidThread());
  audio_device_buffer_ = audioBuffer;
  sample_rate_hz_ = GetNativeSampleRate();
  ALOGD("SetRecordingSampleRate(%d)", sample_rate_hz_);
  audio_device_buffer_->SetRecordingSampleRate(sample_rate_hz_);
  audio_device_buffer_->SetRecordingChannels(kNumChannels);
}

bool AudioRecordJni::BuiltInAECIsAvailable() const {
  ALOGD("BuiltInAECIsAvailable%s", GetThreadInfo().c_str());
  AttachThreadScoped ats(g_jvm);
  JNIEnv* jni = ats.env();
  jmethodID builtInAECIsAvailable = jni->GetStaticMethodID(
      g_audio_record_class, "BuiltInAECIsAvailable", "()Z");
  CHECK_EXCEPTION(jni);
  CHECK(builtInAECIsAvailable);
  jboolean hw_aec = jni->CallStaticBooleanMethod(g_audio_record_class,
                                                 builtInAECIsAvailable);
  CHECK_EXCEPTION(jni);
  return hw_aec;
}

int32_t AudioRecordJni::EnableBuiltInAEC(bool enable) {
  ALOGD("EnableBuiltInAEC%s", GetThreadInfo().c_str());
  DCHECK(thread_checker_.CalledOnValidThread());
  AttachThreadScoped ats(g_jvm);
  JNIEnv* jni = ats.env();
  jmethodID enableBuiltInAEC = GetMethodID(
      jni, g_audio_record_class, "EnableBuiltInAEC", "(Z)Z");
  jboolean res = jni->CallBooleanMethod(
      j_audio_record_, enableBuiltInAEC, enable);
  CHECK_EXCEPTION(jni);
  if (!res) {
    ALOGE("EnableBuiltInAEC failed!");
    return -1;
  }
  return 0;
}

void JNICALL AudioRecordJni::CacheDirectBufferAddress(
    JNIEnv* env, jobject obj, jobject byte_buffer, jlong nativeAudioRecord) {
  webrtc::AudioRecordJni* this_object =
      reinterpret_cast<webrtc::AudioRecordJni*> (nativeAudioRecord);
  this_object->OnCacheDirectBufferAddress(env, byte_buffer);
}

void AudioRecordJni::OnCacheDirectBufferAddress(
    JNIEnv* env, jobject byte_buffer) {
  ALOGD("OnCacheDirectBufferAddress");
  DCHECK(thread_checker_.CalledOnValidThread());
  direct_buffer_address_ =
      env->GetDirectBufferAddress(byte_buffer);
  jlong capacity = env->GetDirectBufferCapacity(byte_buffer);
  ALOGD("direct buffer capacity: %lld", capacity);
  direct_buffer_capacity_in_bytes_ = static_cast<int> (capacity);
}

void JNICALL AudioRecordJni::DataIsRecorded(
  JNIEnv* env, jobject obj, jint length, jlong nativeAudioRecord) {
  webrtc::AudioRecordJni* this_object =
      reinterpret_cast<webrtc::AudioRecordJni*> (nativeAudioRecord  );
  this_object->OnDataIsRecorded(length);
}

// This method is called on a high-priority thread from Java. The name of
// the thread is 'AudioRecordThread'.
void AudioRecordJni::OnDataIsRecorded(int length) {
  DCHECK(thread_checker_java_.CalledOnValidThread());
  audio_device_buffer_->SetRecordedBuffer(direct_buffer_address_,
                                          frames_per_buffer_);
  // TODO(henrika): improve playout delay estimate.
  audio_device_buffer_->SetVQEData(0, kHardwareDelayInMilliseconds, 0);
  audio_device_buffer_->DeliverRecordedData();
}

bool AudioRecordJni::HasDeviceObjects() {
  return (g_jvm && g_context && g_audio_record_class);
}

void AudioRecordJni::CreateJavaInstance() {
  ALOGD("CreateJavaInstance");
  AttachThreadScoped ats(g_jvm);
  JNIEnv* jni = ats.env();
  jmethodID constructorID = GetMethodID(
      jni, g_audio_record_class, "<init>", "(Landroid/content/Context;J)V");
  j_audio_record_ = jni->NewObject(g_audio_record_class,
                                   constructorID,
                                   g_context,
                                   reinterpret_cast<intptr_t>(this));
  CHECK_EXCEPTION(jni) << "Error during NewObject";
  CHECK(j_audio_record_);
  j_audio_record_ = jni->NewGlobalRef(j_audio_record_);
  CHECK_EXCEPTION(jni) << "Error during NewGlobalRef";
  CHECK(j_audio_record_);
}

int AudioRecordJni::GetNativeSampleRate() {
  AttachThreadScoped ats(g_jvm);
  JNIEnv* jni = ats.env();
  jmethodID getNativeSampleRate = GetMethodID(
      jni, g_audio_record_class, "GetNativeSampleRate", "()I");
  jint sample_rate_hz = jni->CallIntMethod(
      j_audio_record_, getNativeSampleRate);
  CHECK_EXCEPTION(jni);
  return sample_rate_hz;
}

}  // namespace webrtc
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2008-2016 the MRtrix3 contributors
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/
 *
 * MRtrix is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * For more details, see www.mrtrix.org
 *
 */

#include "command.h"
#include "progressbar.h"
#include "image.h"
#include "algo/threaded_copy.h"
#include "dwi/gradient.h"
#include "dwi/tensor.h"

using namespace MR;
using namespace App;
using namespace std;

typedef float value_type;
const int default_iter = 2;

void usage ()
{
  ARGUMENTS
  + Argument ("dwi", "the input dwi image.").type_image_in ()
  + Argument ("dt", "the output dt image.").type_image_out ();

  OPTIONS
  + Option ("mask", "only perform computation within the specified binary brain mask image.")
  + Argument ("image").type_image_in()
  + Option ("b0", "the output b0 image.")
  + Argument ("image").type_image_out()
  + Option ("dkt", "the output dkt image.")
  + Argument ("image").type_image_out()
  + Option ("iter","number of iterative reweightings (default: 2); set to 0 for ordinary linear least squares.")
  + Argument ("integer").type_integer (0, default_iter, 10)
  + DWI::GradImportOptions();

  AUTHOR = "Ben Jeurissen (ben.jeurissen@uantwerpen.be)";

  COPYRIGHT = "Copyright (C) 2015 Vision Lab, University of Antwerp, Belgium. "
    "This is free software; see the source for copying conditions. "
    "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.";

  DESCRIPTION
  + "Diffusion (kurtosis) tensor estimation using iteratively reweighted linear least squares estimator.";

  REFERENCES
  + "Veraart, J.; Sijbers, J.; Sunaert, S.; Leemans, A. & Jeurissen, B. " // Internal
    "Weighted linear least squares estimation of diffusion MRI parameters: strengths, limitations, and pitfalls. "
    "NeuroImage, 2013, 81, 335-346";
}

template <class MASKType, class B0Type, class DKTType>
class Processor
{
  public:
    Processor (const Eigen::MatrixXd& b, const int iter, MASKType* mask_image, B0Type* b0_image, DKTType* dkt_image) :
      mask_image (mask_image),
      b0_image (b0_image),
      dkt_image (dkt_image),
      dwi(b.rows()),
      p(b.cols()),
      w(b.rows()),
      work(b.cols(),b.cols()),
      llt(work.rows()),
      b(b),
      maxit(iter) { }

    template <class DWIType, class DTType>
      void operator() (DWIType& dwi_image, DTType& dt_image)
      {
        if (mask_image) {
          assign_pos_of (dwi_image, 0, 3).to (*mask_image);
          if (!mask_image->value())
            return;
        }

        for (auto l = Loop (3) (dwi_image); l; ++l)
          dwi[dwi_image.index(3)] = dwi_image.value();

        double small_intensity = 1.0e-6 * dwi.maxCoeff();
        for (int i = 0; i < dwi.rows(); i++) {
          if (dwi[i] < small_intensity)
            dwi[i] = small_intensity;
          dwi[i] = std::log (dwi[i]);
        }

        work.setZero();
        work.selfadjointView<Eigen::Lower>().rankUpdate (b.transpose());
        p = llt.compute (work.selfadjointView<Eigen::Lower>()).solve(b.transpose()*dwi);
        for (int it = 0; it < maxit; it++) {
          w = (b*p).array().exp();
          work.setZero();
          work.selfadjointView<Eigen::Lower>().rankUpdate (b.transpose()*w.asDiagonal());
          p = llt.compute (work.selfadjointView<Eigen::Lower>()).solve(b.transpose()*w.asDiagonal()*dwi);
        }

        if (b0_image) {
          assign_pos_of (dwi_image, 0, 3).to (*b0_image);
          b0_image->value() = exp(p[6]);
        }

        for (auto l = Loop(3)(dt_image); l; ++l) {
          dt_image.value() = p[dt_image.index(3)];
        }

        if (dkt_image) {
          assign_pos_of (dwi_image, 0, 3).to (*dkt_image);
          double adc_sq = (p[0]+p[1]+p[2])*(p[0]+p[1]+p[2])/9.0;
          for (auto l = Loop(3)(*dkt_image); l; ++l) {
            dkt_image->value() = p[dkt_image->index(3)+7]/adc_sq;
          }
        }
      }

  private:
    copy_ptr<MASKType> mask_image;
    copy_ptr<B0Type> b0_image;
    copy_ptr<DKTType> dkt_image;
    Eigen::VectorXd dwi;
    Eigen::VectorXd p;
    Eigen::VectorXd w;
    Eigen::MatrixXd work;
    Eigen::LLT<Eigen::MatrixXd> llt;
    const Eigen::MatrixXd& b;
    const int maxit;
};

template <class MASKType, class B0Type, class DKTType>
inline Processor<MASKType, B0Type, DKTType> processor (const Eigen::MatrixXd& b, const int& iter, MASKType* mask_image, B0Type* b0_image, DKTType* dkt_image) {
  return { b, iter, mask_image, b0_image, dkt_image };
}

void run ()
{
  auto dwi = Header::open (argument[0]).get_image<value_type>();
  auto grad = DWI::get_valid_DW_scheme (dwi.original_header());

  Image<bool>* mask = nullptr;
  auto opt = get_options ("mask");
  if (opt.size()) {
    mask = new Image<bool> (Image<bool>::open (opt[0][0]));
    check_dimensions (dwi, *mask, 0, 3);
  }

  auto iter = get_option_value ("iter", default_iter);

  auto header = dwi.original_header();
  header.datatype() = DataType::Float32;
  header.set_ndim (4);
  header.size(3) = 6;
  auto dt = Header::create (argument[1], header).get_image<value_type>();

  Image<value_type>* b0 = nullptr;
  opt = get_options ("b0");
  if (opt.size()) {
    header.set_ndim (3);
    b0 = new Image<value_type> (Image<value_type>::create (opt[0][0], header));
  }

  Image<value_type>* dkt = nullptr;
  opt = get_options ("dkt");
  if (opt.size()) {
    header.set_ndim (4);
    header.size(3) = 15;
    dkt = new Image<value_type> (Image<value_type>::create (opt[0][0], header));
  }

  Eigen::MatrixXd b = -DWI::grad2bmatrix<double> (grad, opt.size()>0);

  ThreadedLoop("computing tensors", dwi, 0, 3).run (processor (b, iter, mask, b0, dkt), dwi, dt);
}

<commit_msg>dwi2tensor: Fixing up order of references block<commit_after>/*
 * Copyright (c) 2008-2016 the MRtrix3 contributors
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/
 *
 * MRtrix is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * For more details, see www.mrtrix.org
 *
 */

#include "command.h"
#include "progressbar.h"
#include "image.h"
#include "algo/threaded_copy.h"
#include "dwi/gradient.h"
#include "dwi/tensor.h"

using namespace MR;
using namespace App;
using namespace std;

typedef float value_type;
const int default_iter = 2;

void usage ()
{

  REFERENCES
  +  "Veraart, J.; Sijbers, J.; Sunaert, S.; Leemans, A. & Jeurissen, B. " // Internal
     "Weighted linear least squares estimation of diffusion MRI parameters: strengths, limitations, and pitfalls. "
     "NeuroImage, 2013, 81, 335-346";

  ARGUMENTS
  + Argument ("dwi", "the input dwi image.").type_image_in ()
  + Argument ("dt", "the output dt image.").type_image_out ();

  OPTIONS
  + Option ("mask", "only perform computation within the specified binary brain mask image.")
  + Argument ("image").type_image_in()
  + Option ("b0", "the output b0 image.")
  + Argument ("image").type_image_out()
  + Option ("dkt", "the output dkt image.")
  + Argument ("image").type_image_out()
  + Option ("iter","number of iterative reweightings (default: 2); set to 0 for ordinary linear least squares.")
  + Argument ("integer").type_integer (0, default_iter, 10)
  + DWI::GradImportOptions();

  AUTHOR = "Ben Jeurissen (ben.jeurissen@uantwerpen.be)";

  COPYRIGHT = "Copyright (C) 2015 Vision Lab, University of Antwerp, Belgium. "
    "This is free software; see the source for copying conditions. "
    "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.";

  DESCRIPTION
  + "Diffusion (kurtosis) tensor estimation using iteratively reweighted linear least squares estimator.";
}

template <class MASKType, class B0Type, class DKTType>
class Processor
{
  public:
    Processor (const Eigen::MatrixXd& b, const int iter, MASKType* mask_image, B0Type* b0_image, DKTType* dkt_image) :
      mask_image (mask_image),
      b0_image (b0_image),
      dkt_image (dkt_image),
      dwi(b.rows()),
      p(b.cols()),
      w(b.rows()),
      work(b.cols(),b.cols()),
      llt(work.rows()),
      b(b),
      maxit(iter) { }

    template <class DWIType, class DTType>
      void operator() (DWIType& dwi_image, DTType& dt_image)
      {
        if (mask_image) {
          assign_pos_of (dwi_image, 0, 3).to (*mask_image);
          if (!mask_image->value())
            return;
        }

        for (auto l = Loop (3) (dwi_image); l; ++l)
          dwi[dwi_image.index(3)] = dwi_image.value();

        double small_intensity = 1.0e-6 * dwi.maxCoeff();
        for (int i = 0; i < dwi.rows(); i++) {
          if (dwi[i] < small_intensity)
            dwi[i] = small_intensity;
          dwi[i] = std::log (dwi[i]);
        }

        work.setZero();
        work.selfadjointView<Eigen::Lower>().rankUpdate (b.transpose());
        p = llt.compute (work.selfadjointView<Eigen::Lower>()).solve(b.transpose()*dwi);
        for (int it = 0; it < maxit; it++) {
          w = (b*p).array().exp();
          work.setZero();
          work.selfadjointView<Eigen::Lower>().rankUpdate (b.transpose()*w.asDiagonal());
          p = llt.compute (work.selfadjointView<Eigen::Lower>()).solve(b.transpose()*w.asDiagonal()*dwi);
        }

        if (b0_image) {
          assign_pos_of (dwi_image, 0, 3).to (*b0_image);
          b0_image->value() = exp(p[6]);
        }

        for (auto l = Loop(3)(dt_image); l; ++l) {
          dt_image.value() = p[dt_image.index(3)];
        }

        if (dkt_image) {
          assign_pos_of (dwi_image, 0, 3).to (*dkt_image);
          double adc_sq = (p[0]+p[1]+p[2])*(p[0]+p[1]+p[2])/9.0;
          for (auto l = Loop(3)(*dkt_image); l; ++l) {
            dkt_image->value() = p[dkt_image->index(3)+7]/adc_sq;
          }
        }
      }

  private:
    copy_ptr<MASKType> mask_image;
    copy_ptr<B0Type> b0_image;
    copy_ptr<DKTType> dkt_image;
    Eigen::VectorXd dwi;
    Eigen::VectorXd p;
    Eigen::VectorXd w;
    Eigen::MatrixXd work;
    Eigen::LLT<Eigen::MatrixXd> llt;
    const Eigen::MatrixXd& b;
    const int maxit;
};

template <class MASKType, class B0Type, class DKTType>
inline Processor<MASKType, B0Type, DKTType> processor (const Eigen::MatrixXd& b, const int& iter, MASKType* mask_image, B0Type* b0_image, DKTType* dkt_image) {
  return { b, iter, mask_image, b0_image, dkt_image };
}

void run ()
{
  auto dwi = Header::open (argument[0]).get_image<value_type>();
  auto grad = DWI::get_valid_DW_scheme (dwi.original_header());

  Image<bool>* mask = nullptr;
  auto opt = get_options ("mask");
  if (opt.size()) {
    mask = new Image<bool> (Image<bool>::open (opt[0][0]));
    check_dimensions (dwi, *mask, 0, 3);
  }

  auto iter = get_option_value ("iter", default_iter);

  auto header = dwi.original_header();
  header.datatype() = DataType::Float32;
  header.set_ndim (4);
  header.size(3) = 6;
  auto dt = Header::create (argument[1], header).get_image<value_type>();

  Image<value_type>* b0 = nullptr;
  opt = get_options ("b0");
  if (opt.size()) {
    header.set_ndim (3);
    b0 = new Image<value_type> (Image<value_type>::create (opt[0][0], header));
  }

  Image<value_type>* dkt = nullptr;
  opt = get_options ("dkt");
  if (opt.size()) {
    header.set_ndim (4);
    header.size(3) = 15;
    dkt = new Image<value_type> (Image<value_type>::create (opt[0][0], header));
  }

  Eigen::MatrixXd b = -DWI::grad2bmatrix<double> (grad, opt.size()>0);

  ThreadedLoop("computing tensors", dwi, 0, 3).run (processor (b, iter, mask, b0, dkt), dwi, dt);
}

<|endoftext|>


<commit_before>//===-- llvm/CodeGen/MachineModuleInfo.cpp ----------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/CodeGen/MachineModuleInfo.h"

#include "llvm/Constants.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/DerivedTypes.h"
#include "llvm/GlobalVariable.h"
#include "llvm/Intrinsics.h"
#include "llvm/Instructions.h"
#include "llvm/Module.h"
#include "llvm/Support/Dwarf.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Streams.h"
using namespace llvm;
using namespace llvm::dwarf;

// Handle the Pass registration stuff necessary to use TargetData's.
static RegisterPass<MachineModuleInfo>
X("machinemoduleinfo", "Module Information");
char MachineModuleInfo::ID = 0;

//===----------------------------------------------------------------------===//

MachineModuleInfo::MachineModuleInfo()
: ImmutablePass(&ID)
, LabelIDList()
, FrameMoves()
, LandingPads()
, Personalities()
, CallsEHReturn(0)
, CallsUnwindInit(0)
, DbgInfoAvailable(false)
{
  // Always emit "no personality" info
  Personalities.push_back(NULL);
}
MachineModuleInfo::~MachineModuleInfo() {

}

/// doInitialization - Initialize the state for a new module.
///
bool MachineModuleInfo::doInitialization() {
  return false;
}

/// doFinalization - Tear down the state after completion of a module.
///
bool MachineModuleInfo::doFinalization() {
  return false;
}

/// BeginFunction - Begin gathering function meta information.
///
void MachineModuleInfo::BeginFunction(MachineFunction *MF) {
  // Coming soon.
}

/// EndFunction - Discard function meta information.
///
void MachineModuleInfo::EndFunction() {
  // Clean up frame info.
  FrameMoves.clear();

  // Clean up exception info.
  LandingPads.clear();
  TypeInfos.clear();
  FilterIds.clear();
  FilterEnds.clear();
  CallsEHReturn = 0;
  CallsUnwindInit = 0;
}

/// AnalyzeModule - Scan the module for global debug information.
///
void MachineModuleInfo::AnalyzeModule(Module &M) {
  // Insert functions in the llvm.used array into UsedFunctions.
  GlobalVariable *GV = M.getGlobalVariable("llvm.used");
  if (!GV || !GV->hasInitializer()) return;

  // Should be an array of 'i8*'.
  ConstantArray *InitList = dyn_cast<ConstantArray>(GV->getInitializer());
  if (InitList == 0) return;

  for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) {
    if (ConstantExpr *CE = dyn_cast<ConstantExpr>(InitList->getOperand(i)))
      if (CE->getOpcode() == Instruction::BitCast)
        if (Function *F = dyn_cast<Function>(CE->getOperand(0)))
          UsedFunctions.insert(F);
  }
}

//===-EH-------------------------------------------------------------------===//

/// getOrCreateLandingPadInfo - Find or create an LandingPadInfo for the
/// specified MachineBasicBlock.
LandingPadInfo &MachineModuleInfo::getOrCreateLandingPadInfo
    (MachineBasicBlock *LandingPad) {
  unsigned N = LandingPads.size();
  for (unsigned i = 0; i < N; ++i) {
    LandingPadInfo &LP = LandingPads[i];
    if (LP.LandingPadBlock == LandingPad)
      return LP;
  }

  LandingPads.push_back(LandingPadInfo(LandingPad));
  return LandingPads[N];
}

/// addInvoke - Provide the begin and end labels of an invoke style call and
/// associate it with a try landing pad block.
void MachineModuleInfo::addInvoke(MachineBasicBlock *LandingPad,
                                  unsigned BeginLabel, unsigned EndLabel) {
  LandingPadInfo &LP = getOrCreateLandingPadInfo(LandingPad);
  LP.BeginLabels.push_back(BeginLabel);
  LP.EndLabels.push_back(EndLabel);
}

/// addLandingPad - Provide the label of a try LandingPad block.
///
unsigned MachineModuleInfo::addLandingPad(MachineBasicBlock *LandingPad) {
  unsigned LandingPadLabel = NextLabelID();
  LandingPadInfo &LP = getOrCreateLandingPadInfo(LandingPad);
  LP.LandingPadLabel = LandingPadLabel;
  return LandingPadLabel;
}

/// addPersonality - Provide the personality function for the exception
/// information.
void MachineModuleInfo::addPersonality(MachineBasicBlock *LandingPad,
                                       Function *Personality) {
  LandingPadInfo &LP = getOrCreateLandingPadInfo(LandingPad);
  LP.Personality = Personality;

  for (unsigned i = 0; i < Personalities.size(); ++i)
    if (Personalities[i] == Personality)
      return;

  Personalities.push_back(Personality);
}

/// addCatchTypeInfo - Provide the catch typeinfo for a landing pad.
///
void MachineModuleInfo::addCatchTypeInfo(MachineBasicBlock *LandingPad,
                                        std::vector<GlobalVariable *> &TyInfo) {
  LandingPadInfo &LP = getOrCreateLandingPadInfo(LandingPad);
  for (unsigned N = TyInfo.size(); N; --N)
    LP.TypeIds.push_back(getTypeIDFor(TyInfo[N - 1]));
}

/// addFilterTypeInfo - Provide the filter typeinfo for a landing pad.
///
void MachineModuleInfo::addFilterTypeInfo(MachineBasicBlock *LandingPad,
                                        std::vector<GlobalVariable *> &TyInfo) {
  LandingPadInfo &LP = getOrCreateLandingPadInfo(LandingPad);
  std::vector<unsigned> IdsInFilter(TyInfo.size());
  for (unsigned I = 0, E = TyInfo.size(); I != E; ++I)
    IdsInFilter[I] = getTypeIDFor(TyInfo[I]);
  LP.TypeIds.push_back(getFilterIDFor(IdsInFilter));
}

/// addCleanup - Add a cleanup action for a landing pad.
///
void MachineModuleInfo::addCleanup(MachineBasicBlock *LandingPad) {
  LandingPadInfo &LP = getOrCreateLandingPadInfo(LandingPad);
  LP.TypeIds.push_back(0);
}

/// TidyLandingPads - Remap landing pad labels and remove any deleted landing
/// pads.
void MachineModuleInfo::TidyLandingPads() {
  for (unsigned i = 0; i != LandingPads.size(); ) {
    LandingPadInfo &LandingPad = LandingPads[i];
    LandingPad.LandingPadLabel = MappedLabel(LandingPad.LandingPadLabel);

    // Special case: we *should* emit LPs with null LP MBB. This indicates
    // "nounwind" case.
    if (!LandingPad.LandingPadLabel && LandingPad.LandingPadBlock) {
      LandingPads.erase(LandingPads.begin() + i);
      continue;
    }

    for (unsigned j=0; j != LandingPads[i].BeginLabels.size(); ) {
      unsigned BeginLabel = MappedLabel(LandingPad.BeginLabels[j]);
      unsigned EndLabel = MappedLabel(LandingPad.EndLabels[j]);

      if (!BeginLabel || !EndLabel) {
        LandingPad.BeginLabels.erase(LandingPad.BeginLabels.begin() + j);
        LandingPad.EndLabels.erase(LandingPad.EndLabels.begin() + j);
        continue;
      }

      LandingPad.BeginLabels[j] = BeginLabel;
      LandingPad.EndLabels[j] = EndLabel;
      ++j;
    }

    // Remove landing pads with no try-ranges.
    if (LandingPads[i].BeginLabels.empty()) {
      LandingPads.erase(LandingPads.begin() + i);
      continue;
    }

    // If there is no landing pad, ensure that the list of typeids is empty.
    // If the only typeid is a cleanup, this is the same as having no typeids.
    if (!LandingPad.LandingPadBlock ||
        (LandingPad.TypeIds.size() == 1 && !LandingPad.TypeIds[0]))
      LandingPad.TypeIds.clear();

    ++i;
  }
}

/// getTypeIDFor - Return the type id for the specified typeinfo.  This is
/// function wide.
unsigned MachineModuleInfo::getTypeIDFor(GlobalVariable *TI) {
  for (unsigned i = 0, N = TypeInfos.size(); i != N; ++i)
    if (TypeInfos[i] == TI) return i + 1;

  TypeInfos.push_back(TI);
  return TypeInfos.size();
}

/// getFilterIDFor - Return the filter id for the specified typeinfos.  This is
/// function wide.
int MachineModuleInfo::getFilterIDFor(std::vector<unsigned> &TyIds) {
  // If the new filter coincides with the tail of an existing filter, then
  // re-use the existing filter.  Folding filters more than this requires
  // re-ordering filters and/or their elements - probably not worth it.
  for (std::vector<unsigned>::iterator I = FilterEnds.begin(),
       E = FilterEnds.end(); I != E; ++I) {
    unsigned i = *I, j = TyIds.size();

    while (i && j)
      if (FilterIds[--i] != TyIds[--j])
        goto try_next;

    if (!j)
      // The new filter coincides with range [i, end) of the existing filter.
      return -(1 + i);

try_next:;
  }

  // Add the new filter.
  int FilterID = -(1 + FilterIds.size());
  FilterIds.reserve(FilterIds.size() + TyIds.size() + 1);
  for (unsigned I = 0, N = TyIds.size(); I != N; ++I)
    FilterIds.push_back(TyIds[I]);
  FilterEnds.push_back(FilterIds.size());
  FilterIds.push_back(0); // terminator
  return FilterID;
}

/// getPersonality - Return the personality function for the current function.
Function *MachineModuleInfo::getPersonality() const {
  // FIXME: Until PR1414 will be fixed, we're using 1 personality function per
  // function
  return !LandingPads.empty() ? LandingPads[0].Personality : NULL;
}

/// getPersonalityIndex - Return unique index for current personality
/// function. NULL personality function should always get zero index.
unsigned MachineModuleInfo::getPersonalityIndex() const {
  const Function* Personality = NULL;

  // Scan landing pads. If there is at least one non-NULL personality - use it.
  for (unsigned i = 0; i != LandingPads.size(); ++i)
    if (LandingPads[i].Personality) {
      Personality = LandingPads[i].Personality;
      break;
    }

  for (unsigned i = 0; i < Personalities.size(); ++i) {
    if (Personalities[i] == Personality)
      return i;
  }

  // This should never happen
  llvm_unreachable("Personality function should be set!");
  return 0;
}

//===----------------------------------------------------------------------===//
/// DebugLabelFolding pass - This pass prunes out redundant labels.  This allows
/// a info consumer to determine if the range of two labels is empty, by seeing
/// if the labels map to the same reduced label.

namespace llvm {

struct DebugLabelFolder : public MachineFunctionPass {
  static char ID;
  DebugLabelFolder() : MachineFunctionPass(&ID) {}

  virtual void getAnalysisUsage(AnalysisUsage &AU) const {
    AU.addPreservedID(MachineLoopInfoID);
    AU.addPreservedID(MachineDominatorsID);
    MachineFunctionPass::getAnalysisUsage(AU);
  }

  virtual bool runOnMachineFunction(MachineFunction &MF);
  virtual const char *getPassName() const { return "Label Folder"; }
};

char DebugLabelFolder::ID = 0;

bool DebugLabelFolder::runOnMachineFunction(MachineFunction &MF) {
  // Get machine module info.
  MachineModuleInfo *MMI = getAnalysisIfAvailable<MachineModuleInfo>();
  if (!MMI) return false;

  // Track if change is made.
  bool MadeChange = false;
  // No prior label to begin.
  unsigned PriorLabel = 0;

  // Iterate through basic blocks.
  for (MachineFunction::iterator BB = MF.begin(), E = MF.end();
       BB != E; ++BB) {
    // Iterate through instructions.
    for (MachineBasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ) {
      // Is it a label.
      if (I->isDebugLabel() && !MMI->isDbgLabelUsed(I->getOperand(0).getImm())){
        // The label ID # is always operand #0, an immediate.
        unsigned NextLabel = I->getOperand(0).getImm();

        // If there was an immediate prior label.
        if (PriorLabel) {
          // Remap the current label to prior label.
          MMI->RemapLabel(NextLabel, PriorLabel);
          // Delete the current label.
          I = BB->erase(I);
          // Indicate a change has been made.
          MadeChange = true;
          continue;
        } else {
          // Start a new round.
          PriorLabel = NextLabel;
        }
       } else {
        // No consecutive labels.
        PriorLabel = 0;
      }

      ++I;
    }
  }

  return MadeChange;
}

FunctionPass *createDebugLabelFoldingPass() { return new DebugLabelFolder(); }

}

<commit_msg>use stripPointerCasts to simplify some code.<commit_after>//===-- llvm/CodeGen/MachineModuleInfo.cpp ----------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/CodeGen/MachineModuleInfo.h"

#include "llvm/Constants.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/DerivedTypes.h"
#include "llvm/GlobalVariable.h"
#include "llvm/Intrinsics.h"
#include "llvm/Instructions.h"
#include "llvm/Module.h"
#include "llvm/Support/Dwarf.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Streams.h"
using namespace llvm;
using namespace llvm::dwarf;

// Handle the Pass registration stuff necessary to use TargetData's.
static RegisterPass<MachineModuleInfo>
X("machinemoduleinfo", "Module Information");
char MachineModuleInfo::ID = 0;

//===----------------------------------------------------------------------===//

MachineModuleInfo::MachineModuleInfo()
: ImmutablePass(&ID)
, LabelIDList()
, FrameMoves()
, LandingPads()
, Personalities()
, CallsEHReturn(0)
, CallsUnwindInit(0)
, DbgInfoAvailable(false)
{
  // Always emit "no personality" info
  Personalities.push_back(NULL);
}
MachineModuleInfo::~MachineModuleInfo() {

}

/// doInitialization - Initialize the state for a new module.
///
bool MachineModuleInfo::doInitialization() {
  return false;
}

/// doFinalization - Tear down the state after completion of a module.
///
bool MachineModuleInfo::doFinalization() {
  return false;
}

/// BeginFunction - Begin gathering function meta information.
///
void MachineModuleInfo::BeginFunction(MachineFunction *MF) {
  // Coming soon.
}

/// EndFunction - Discard function meta information.
///
void MachineModuleInfo::EndFunction() {
  // Clean up frame info.
  FrameMoves.clear();

  // Clean up exception info.
  LandingPads.clear();
  TypeInfos.clear();
  FilterIds.clear();
  FilterEnds.clear();
  CallsEHReturn = 0;
  CallsUnwindInit = 0;
}

/// AnalyzeModule - Scan the module for global debug information.
///
void MachineModuleInfo::AnalyzeModule(Module &M) {
  // Insert functions in the llvm.used array into UsedFunctions.
  GlobalVariable *GV = M.getGlobalVariable("llvm.used");
  if (!GV || !GV->hasInitializer()) return;

  // Should be an array of 'i8*'.
  ConstantArray *InitList = dyn_cast<ConstantArray>(GV->getInitializer());
  if (InitList == 0) return;

  for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i)
    if (Function *F =
          dyn_cast<Function>(InitList->getOperand(i)->stripPointerCasts()))
      UsedFunctions.insert(F);
}

//===-EH-------------------------------------------------------------------===//

/// getOrCreateLandingPadInfo - Find or create an LandingPadInfo for the
/// specified MachineBasicBlock.
LandingPadInfo &MachineModuleInfo::getOrCreateLandingPadInfo
    (MachineBasicBlock *LandingPad) {
  unsigned N = LandingPads.size();
  for (unsigned i = 0; i < N; ++i) {
    LandingPadInfo &LP = LandingPads[i];
    if (LP.LandingPadBlock == LandingPad)
      return LP;
  }

  LandingPads.push_back(LandingPadInfo(LandingPad));
  return LandingPads[N];
}

/// addInvoke - Provide the begin and end labels of an invoke style call and
/// associate it with a try landing pad block.
void MachineModuleInfo::addInvoke(MachineBasicBlock *LandingPad,
                                  unsigned BeginLabel, unsigned EndLabel) {
  LandingPadInfo &LP = getOrCreateLandingPadInfo(LandingPad);
  LP.BeginLabels.push_back(BeginLabel);
  LP.EndLabels.push_back(EndLabel);
}

/// addLandingPad - Provide the label of a try LandingPad block.
///
unsigned MachineModuleInfo::addLandingPad(MachineBasicBlock *LandingPad) {
  unsigned LandingPadLabel = NextLabelID();
  LandingPadInfo &LP = getOrCreateLandingPadInfo(LandingPad);
  LP.LandingPadLabel = LandingPadLabel;
  return LandingPadLabel;
}

/// addPersonality - Provide the personality function for the exception
/// information.
void MachineModuleInfo::addPersonality(MachineBasicBlock *LandingPad,
                                       Function *Personality) {
  LandingPadInfo &LP = getOrCreateLandingPadInfo(LandingPad);
  LP.Personality = Personality;

  for (unsigned i = 0; i < Personalities.size(); ++i)
    if (Personalities[i] == Personality)
      return;

  Personalities.push_back(Personality);
}

/// addCatchTypeInfo - Provide the catch typeinfo for a landing pad.
///
void MachineModuleInfo::addCatchTypeInfo(MachineBasicBlock *LandingPad,
                                        std::vector<GlobalVariable *> &TyInfo) {
  LandingPadInfo &LP = getOrCreateLandingPadInfo(LandingPad);
  for (unsigned N = TyInfo.size(); N; --N)
    LP.TypeIds.push_back(getTypeIDFor(TyInfo[N - 1]));
}

/// addFilterTypeInfo - Provide the filter typeinfo for a landing pad.
///
void MachineModuleInfo::addFilterTypeInfo(MachineBasicBlock *LandingPad,
                                        std::vector<GlobalVariable *> &TyInfo) {
  LandingPadInfo &LP = getOrCreateLandingPadInfo(LandingPad);
  std::vector<unsigned> IdsInFilter(TyInfo.size());
  for (unsigned I = 0, E = TyInfo.size(); I != E; ++I)
    IdsInFilter[I] = getTypeIDFor(TyInfo[I]);
  LP.TypeIds.push_back(getFilterIDFor(IdsInFilter));
}

/// addCleanup - Add a cleanup action for a landing pad.
///
void MachineModuleInfo::addCleanup(MachineBasicBlock *LandingPad) {
  LandingPadInfo &LP = getOrCreateLandingPadInfo(LandingPad);
  LP.TypeIds.push_back(0);
}

/// TidyLandingPads - Remap landing pad labels and remove any deleted landing
/// pads.
void MachineModuleInfo::TidyLandingPads() {
  for (unsigned i = 0; i != LandingPads.size(); ) {
    LandingPadInfo &LandingPad = LandingPads[i];
    LandingPad.LandingPadLabel = MappedLabel(LandingPad.LandingPadLabel);

    // Special case: we *should* emit LPs with null LP MBB. This indicates
    // "nounwind" case.
    if (!LandingPad.LandingPadLabel && LandingPad.LandingPadBlock) {
      LandingPads.erase(LandingPads.begin() + i);
      continue;
    }

    for (unsigned j=0; j != LandingPads[i].BeginLabels.size(); ) {
      unsigned BeginLabel = MappedLabel(LandingPad.BeginLabels[j]);
      unsigned EndLabel = MappedLabel(LandingPad.EndLabels[j]);

      if (!BeginLabel || !EndLabel) {
        LandingPad.BeginLabels.erase(LandingPad.BeginLabels.begin() + j);
        LandingPad.EndLabels.erase(LandingPad.EndLabels.begin() + j);
        continue;
      }

      LandingPad.BeginLabels[j] = BeginLabel;
      LandingPad.EndLabels[j] = EndLabel;
      ++j;
    }

    // Remove landing pads with no try-ranges.
    if (LandingPads[i].BeginLabels.empty()) {
      LandingPads.erase(LandingPads.begin() + i);
      continue;
    }

    // If there is no landing pad, ensure that the list of typeids is empty.
    // If the only typeid is a cleanup, this is the same as having no typeids.
    if (!LandingPad.LandingPadBlock ||
        (LandingPad.TypeIds.size() == 1 && !LandingPad.TypeIds[0]))
      LandingPad.TypeIds.clear();

    ++i;
  }
}

/// getTypeIDFor - Return the type id for the specified typeinfo.  This is
/// function wide.
unsigned MachineModuleInfo::getTypeIDFor(GlobalVariable *TI) {
  for (unsigned i = 0, N = TypeInfos.size(); i != N; ++i)
    if (TypeInfos[i] == TI) return i + 1;

  TypeInfos.push_back(TI);
  return TypeInfos.size();
}

/// getFilterIDFor - Return the filter id for the specified typeinfos.  This is
/// function wide.
int MachineModuleInfo::getFilterIDFor(std::vector<unsigned> &TyIds) {
  // If the new filter coincides with the tail of an existing filter, then
  // re-use the existing filter.  Folding filters more than this requires
  // re-ordering filters and/or their elements - probably not worth it.
  for (std::vector<unsigned>::iterator I = FilterEnds.begin(),
       E = FilterEnds.end(); I != E; ++I) {
    unsigned i = *I, j = TyIds.size();

    while (i && j)
      if (FilterIds[--i] != TyIds[--j])
        goto try_next;

    if (!j)
      // The new filter coincides with range [i, end) of the existing filter.
      return -(1 + i);

try_next:;
  }

  // Add the new filter.
  int FilterID = -(1 + FilterIds.size());
  FilterIds.reserve(FilterIds.size() + TyIds.size() + 1);
  for (unsigned I = 0, N = TyIds.size(); I != N; ++I)
    FilterIds.push_back(TyIds[I]);
  FilterEnds.push_back(FilterIds.size());
  FilterIds.push_back(0); // terminator
  return FilterID;
}

/// getPersonality - Return the personality function for the current function.
Function *MachineModuleInfo::getPersonality() const {
  // FIXME: Until PR1414 will be fixed, we're using 1 personality function per
  // function
  return !LandingPads.empty() ? LandingPads[0].Personality : NULL;
}

/// getPersonalityIndex - Return unique index for current personality
/// function. NULL personality function should always get zero index.
unsigned MachineModuleInfo::getPersonalityIndex() const {
  const Function* Personality = NULL;

  // Scan landing pads. If there is at least one non-NULL personality - use it.
  for (unsigned i = 0; i != LandingPads.size(); ++i)
    if (LandingPads[i].Personality) {
      Personality = LandingPads[i].Personality;
      break;
    }

  for (unsigned i = 0; i < Personalities.size(); ++i) {
    if (Personalities[i] == Personality)
      return i;
  }

  // This should never happen
  llvm_unreachable("Personality function should be set!");
  return 0;
}

//===----------------------------------------------------------------------===//
/// DebugLabelFolding pass - This pass prunes out redundant labels.  This allows
/// a info consumer to determine if the range of two labels is empty, by seeing
/// if the labels map to the same reduced label.

namespace llvm {

struct DebugLabelFolder : public MachineFunctionPass {
  static char ID;
  DebugLabelFolder() : MachineFunctionPass(&ID) {}

  virtual void getAnalysisUsage(AnalysisUsage &AU) const {
    AU.addPreservedID(MachineLoopInfoID);
    AU.addPreservedID(MachineDominatorsID);
    MachineFunctionPass::getAnalysisUsage(AU);
  }

  virtual bool runOnMachineFunction(MachineFunction &MF);
  virtual const char *getPassName() const { return "Label Folder"; }
};

char DebugLabelFolder::ID = 0;

bool DebugLabelFolder::runOnMachineFunction(MachineFunction &MF) {
  // Get machine module info.
  MachineModuleInfo *MMI = getAnalysisIfAvailable<MachineModuleInfo>();
  if (!MMI) return false;

  // Track if change is made.
  bool MadeChange = false;
  // No prior label to begin.
  unsigned PriorLabel = 0;

  // Iterate through basic blocks.
  for (MachineFunction::iterator BB = MF.begin(), E = MF.end();
       BB != E; ++BB) {
    // Iterate through instructions.
    for (MachineBasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ) {
      // Is it a label.
      if (I->isDebugLabel() && !MMI->isDbgLabelUsed(I->getOperand(0).getImm())){
        // The label ID # is always operand #0, an immediate.
        unsigned NextLabel = I->getOperand(0).getImm();

        // If there was an immediate prior label.
        if (PriorLabel) {
          // Remap the current label to prior label.
          MMI->RemapLabel(NextLabel, PriorLabel);
          // Delete the current label.
          I = BB->erase(I);
          // Indicate a change has been made.
          MadeChange = true;
          continue;
        } else {
          // Start a new round.
          PriorLabel = NextLabel;
        }
       } else {
        // No consecutive labels.
        PriorLabel = 0;
      }

      ++I;
    }
  }

  return MadeChange;
}

FunctionPass *createDebugLabelFoldingPass() { return new DebugLabelFolder(); }

}

<|endoftext|>


<commit_before>// Author:  Ben Lau (https://github.com/benlau)
#include <QCoreApplication>
#include <QPointer>
#include <QtCore>
#include <QPair>
#include <QQueue>
#include "systemdispatcher.h"

using namespace AndroidNative;

static QPointer<SystemDispatcher> m_instance;

QString SystemDispatcher::ACTIVITY_RESUME_MESSAGE = "Activity.onResume";
QString SystemDispatcher::ACTIVITY_RESULT_MESSAGE = "Activity.onActivityResult";
static bool registerNativesCalled = false;

#ifdef Q_OS_ANDROID
#include <QAndroidJniObject>
#include <QAndroidJniEnvironment>

#define JCLASS_Name "androidnative/SystemDispatcher"
#define DISPATCH_SIGNATURE "(Ljava/lang/String;Ljava/util/Map;)V"
#define EMIT_SIGNATURE "(Ljava/lang/String;Ljava/util/Map;)V"

static QVariantMap createVariantMap(jobject data);
static jobject createHashMap(const QVariantMap &data);

static QVariant convertToQVariant(QAndroidJniObject value) {
    QVariant v;
    if (!value.isValid()) {
        return v;
    }

    QAndroidJniEnvironment env;

    jclass jclass_of_string = env->FindClass("java/lang/String");
    jclass jclass_of_integer = env->FindClass("java/lang/Integer");
    jclass jclass_of_boolean = env->FindClass("java/lang/Boolean");
    jclass jclass_of_list = env->FindClass("java/util/List");
    jclass jclass_of_map = env->FindClass("java/util/Map");

    if (env->IsInstanceOf(value.object<jobject>(),jclass_of_boolean)) {
        v = QVariant::fromValue<bool>(value.callMethod<jboolean>("booleanValue","()Z"));
    } else if (env->IsInstanceOf(value.object<jobject>(),jclass_of_integer)) {
        v = value.callMethod<jint>("intValue","()I");
    } else if (env->IsInstanceOf(value.object<jobject>(),jclass_of_string)) {
        v = value.toString();
    } else if (env->IsInstanceOf(value.object<jobject>(), jclass_of_map)) {
        v = createVariantMap(value.object<jobject>());
    } else if (env->IsInstanceOf(value.object<jobject>(),jclass_of_list)) {
        QVariantList list;
        int count = value.callMethod<jint>("size","()I");
        for (int i = 0 ; i < count ; i++) {
            QAndroidJniObject item = value.callObjectMethod("get","(I)Ljava/lang/Object;",i);
            list.append(convertToQVariant(item));
        }
        v = list;
    }

    env->DeleteLocalRef(jclass_of_string);
    env->DeleteLocalRef(jclass_of_integer);
    env->DeleteLocalRef(jclass_of_boolean);
    env->DeleteLocalRef(jclass_of_list);
    env->DeleteLocalRef(jclass_of_map);

    return v;
}

static QVariantMap createVariantMap(jobject data) {
    QVariantMap res;

    QAndroidJniEnvironment env;
    /* Reference : https://community.oracle.com/thread/1549999 */

    // Get the HashMap Class
    jclass jclass_of_hashmap = (env)->GetObjectClass(data);

    // Get link to Method "entrySet"
    jmethodID entrySetMethod = (env)->GetMethodID(jclass_of_hashmap, "entrySet", "()Ljava/util/Set;");

    // Invoke the "entrySet" method on the HashMap object
    jobject jobject_of_entryset = env->CallObjectMethod(data, entrySetMethod);

    // Get the Set Class
    jclass jclass_of_set = (env)->FindClass("java/util/Set"); // Problem during compilation !!!!!

    if (jclass_of_set == 0) {
         qWarning() << "java/util/Set lookup failed\n";
         return res;
    }

    // Get link to Method "iterator"
    jmethodID iteratorMethod = env->GetMethodID(jclass_of_set, "iterator", "()Ljava/util/Iterator;");

    // Invoke the "iterator" method on the jobject_of_entryset variable of type Set
    jobject jobject_of_iterator = env->CallObjectMethod(jobject_of_entryset, iteratorMethod);

    // Get the "Iterator" class
    jclass jclass_of_iterator = (env)->FindClass("java/util/Iterator");

    // Get link to Method "hasNext"
    jmethodID hasNextMethod = env->GetMethodID(jclass_of_iterator, "hasNext", "()Z");

    jmethodID nextMethod = env->GetMethodID(jclass_of_iterator, "next", "()Ljava/lang/Object;");

    while (env->CallBooleanMethod(jobject_of_iterator, hasNextMethod) ) {
        jobject jEntry = env->CallObjectMethod(jobject_of_iterator,nextMethod);
        QAndroidJniObject entry = QAndroidJniObject(jEntry);
        QAndroidJniObject key = entry.callObjectMethod("getKey","()Ljava/lang/Object;");
        QAndroidJniObject value = entry.callObjectMethod("getValue","()Ljava/lang/Object;");
        QString k = key.toString();

        QVariant v = convertToQVariant(value);

        env->DeleteLocalRef(jEntry);

        if (v.isNull()) {
            continue;
        }

        res[k] = v;
    }

    if (env->ExceptionOccurred()) {
        env->ExceptionDescribe();
        env->ExceptionClear();
    }

    env->DeleteLocalRef(jclass_of_hashmap);
    env->DeleteLocalRef(jobject_of_entryset);
    env->DeleteLocalRef(jclass_of_set);
    env->DeleteLocalRef(jobject_of_iterator);
    env->DeleteLocalRef(jclass_of_iterator);

    return res;
}

static jobject convertToJObject(QVariant v) {
    jobject res = 0;
    QAndroidJniEnvironment env;

    if (v.type() == QVariant::String) {
        QString str = v.toString();
        res = env->NewStringUTF(str.toLocal8Bit().data());
    } else if (v.type() == QVariant::Int) {
        jclass integerClass = env->FindClass("java/lang/Integer");
        jmethodID integerConstructor = env->GetMethodID(integerClass, "<init>", "(I)V");

        res = env->NewObject(integerClass,integerConstructor,v.toInt());

        env->DeleteLocalRef(integerClass);
    } else if (v.type() == QVariant::Bool) {
        jclass booleanClass = env->FindClass("java/lang/Boolean");
        jmethodID booleanConstructor = env->GetMethodID(booleanClass,"<init>","(Z)V");

        res = env->NewObject(booleanClass,booleanConstructor,v.toBool());

        env->DeleteLocalRef(booleanClass);

    } else if (v.type() == QVariant::Map) {
        res = createHashMap(v.toMap());
    } else  if (v.type() == QVariant::List){
        QVariantList list = v.value<QVariantList>();
        jclass arrayListClass = env->FindClass("java/util/ArrayList");
        jmethodID init = env->GetMethodID(arrayListClass, "<init>", "(I)V");
        res = env->NewObject( arrayListClass, init, list.size());

        jmethodID add = env->GetMethodID( arrayListClass, "add",
                    "(Ljava/lang/Object;)Z");

        for (int i = 0 ; i < list.size() ; i++) {
            jobject item = convertToJObject(list.at(i));
            env->CallBooleanMethod(res,add, item);
            env->DeleteLocalRef(item);
        }

        env->DeleteLocalRef(arrayListClass);
    } else {
        qWarning() << "ANSystemDispatcher: Non-supported data type - " <<  v.type();
    }
    return res;
}

static jobject createHashMap(const QVariantMap &data) {
    QAndroidJniEnvironment env;

    jclass mapClass = env->FindClass("java/util/HashMap");

    if (mapClass == NULL)  {
        qWarning() << "Failed to find class" << "java/util/HashMap";
        return NULL;
    }

    jsize map_len = data.size();

    jmethodID init = env->GetMethodID(mapClass, "<init>", "(I)V");
    jobject hashMap = env->NewObject( mapClass, init, map_len);

    jmethodID put = env->GetMethodID( mapClass, "put",
                "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");

    QMapIterator<QString, QVariant> iter(data);
    while (iter.hasNext()) {
        iter.next();

        QString key = iter.key();
        jstring jkey = env->NewStringUTF(key.toLocal8Bit().data());
        QVariant v = iter.value();
        jobject item = convertToJObject(v);

        if (item == 0) {
            continue;
        }

        env->CallObjectMethod(hashMap,put,jkey,item);
        env->DeleteLocalRef(item);
        env->DeleteLocalRef(jkey);
     }

    if (env->ExceptionOccurred()) {
        env->ExceptionDescribe();
        env->ExceptionClear();
    }

    env->DeleteLocalRef(mapClass);

    return hashMap;
}

static void jniEmit(JNIEnv* env,jobject object,jstring name,jobject data) {
    Q_UNUSED(object);
    Q_UNUSED(env);

    QAndroidJniObject tmp(name);
    QString str = tmp.toString();

    QVariantMap map;

    if (data != 0) {
        map = createVariantMap(data);
    }

    if (m_instance.isNull()) {
        return;
    }

    QMetaObject::invokeMethod(m_instance.data(),"dispatched",Qt::AutoConnection,
                              Q_ARG(QString, str),
                              Q_ARG(QVariantMap,map));
}

static void printInstruction() {
    static bool printOnce = false;

    if (printOnce) {
        return;
    }
    printOnce = true;

    // BuildMyString.com generated code. Please enjoy your string responsibly.

    QString msg =  "Using SystemDispatcher in Android but SystemDispatcher::registerNatives() is never called. Instruction: \r\n"
           "https://github.com/benlau/androidnative.pri/blob/master/docs/installation.md\r\n";

   qWarning() << msg;
}
#endif

AndroidNative::SystemDispatcher::SystemDispatcher(QObject* parent) : QObject(parent)
{
}

AndroidNative::SystemDispatcher::~SystemDispatcher()
{
}

AndroidNative::SystemDispatcher *SystemDispatcher::instance()
{
    if (!m_instance) {
        QCoreApplication* app = QCoreApplication::instance();
        m_instance = new SystemDispatcher(app);
    }
    return m_instance;
}

void AndroidNative::SystemDispatcher::dispatch(QString type, QVariantMap message)
{
    Q_UNUSED(type);
    Q_UNUSED(message);

#ifdef Q_OS_ANDROID
    if (!registerNativesCalled) {
        printInstruction();
    }

    QAndroidJniEnvironment env;

    jstring jType = env->NewStringUTF(type.toLocal8Bit().data());
    jobject jData = createHashMap(message);
    QAndroidJniObject::callStaticMethod<void>(JCLASS_Name, "dispatch",
                                              DISPATCH_SIGNATURE,
                                              jType,jData);
    env->DeleteLocalRef(jType);
    env->DeleteLocalRef(jData);

#else
    static bool dispatching = false;
    static QQueue<QPair<QString,QVariantMap> > queue;

    if (dispatching) {
        queue.enqueue(QPair<QString,QVariantMap> (type,message) );
        return;
    }

    dispatching = true;
    emit dispatched(type,message);

    while (queue.size() > 0) {
        QPair<QString,QVariantMap> pair = queue.dequeue();
        emit dispatched(pair.first,pair.second);
    }
    dispatching = false;
#endif
}

void AndroidNative::SystemDispatcher::loadClass(QString javaClassName)
{
    QVariantMap message;
    message["className"] = javaClassName;

    dispatch("androidnative.SystemDispatcher.loadClass",message);
}

void AndroidNative::SystemDispatcher::registerNatives()
{
    Q_UNUSED(registerNativesCalled);

#ifdef Q_OS_ANDROID
    registerNativesCalled = true;

    QAndroidJniEnvironment env;
    jclass clazz = env->FindClass(JCLASS_Name);
    if (!clazz)
    {
        qCritical() <<  "androidNative.SystemDispatcher Java class is not found. Please configure your build.gradle according to this instruction: \r\n"
                        "https://github.com/benlau/androidnative.pri/blob/master/docs/installation.md\r\n";
        if (env->ExceptionOccurred()) {
            env->ExceptionDescribe();
            env->ExceptionClear();
        }
        return;
    }

    JNINativeMethod methods[] =
    {
        {"jniEmit", EMIT_SIGNATURE, (void *)&jniEmit},
    };

    int numMethods = sizeof(methods) / sizeof(methods[0]);
    if (env->RegisterNatives(clazz, methods, numMethods) < 0) {
        if (env->ExceptionOccurred()) {
            env->ExceptionDescribe();
            env->ExceptionClear();
            qCritical() << "Exception occurred!!!";
            return;
        }
    }

    QAndroidJniObject::callStaticMethod<void>(JCLASS_Name, "init",
                                              "()V");
#endif
}
<commit_msg>support for long,float and double data types in systemdispatcher<commit_after>// Author:  Ben Lau (https://github.com/benlau)
#include <QCoreApplication>
#include <QPointer>
#include <QtCore>
#include <QPair>
#include <QQueue>
#include "systemdispatcher.h"

using namespace AndroidNative;

static QPointer<SystemDispatcher> m_instance;

QString SystemDispatcher::ACTIVITY_RESUME_MESSAGE = "Activity.onResume";
QString SystemDispatcher::ACTIVITY_RESULT_MESSAGE = "Activity.onActivityResult";
static bool registerNativesCalled = false;

#ifdef Q_OS_ANDROID
#include <QAndroidJniObject>
#include <QAndroidJniEnvironment>

#define JCLASS_Name "androidnative/SystemDispatcher"
#define DISPATCH_SIGNATURE "(Ljava/lang/String;Ljava/util/Map;)V"
#define EMIT_SIGNATURE "(Ljava/lang/String;Ljava/util/Map;)V"

static QVariantMap createVariantMap(jobject data);
static jobject createHashMap(const QVariantMap &data);

static QVariant convertToQVariant(QAndroidJniObject value) {
    QVariant v;
    if (!value.isValid()) {
        return v;
    }

    QAndroidJniEnvironment env;

    jclass jclass_of_string = env->FindClass("java/lang/String");
    jclass jclass_of_integer = env->FindClass("java/lang/Integer");
    jclass jclass_of_long = env->FindClass("java/lang/Long");
    jclass jclass_of_float = env->FindClass("java/lang/Float");
    jclass jclass_of_double = env->FindClass("java/lang/Double");
    jclass jclass_of_boolean = env->FindClass("java/lang/Boolean");
    jclass jclass_of_list = env->FindClass("java/util/List");
    jclass jclass_of_map = env->FindClass("java/util/Map");

    if (env->IsInstanceOf(value.object<jobject>(),jclass_of_boolean)) {
        v = QVariant::fromValue<bool>(value.callMethod<jboolean>("booleanValue","()Z"));
    } else if (env->IsInstanceOf(value.object<jobject>(),jclass_of_integer)) {
        v = value.callMethod<jint>("intValue","()I");
    } else if (env->IsInstanceOf(value.object<jobject>(),jclass_of_string)) {
        v = value.toString();
    } else if (env->IsInstanceOf(value.object<jobject>(),jclass_of_long)) {
        v = value.callMethod<jlong>("longValue","()J");
    } else if (env->IsInstanceOf(value.object<jobject>(),jclass_of_float)) {
        v = value.callMethod<jfloat>("floatValue","()F");
    } else if (env->IsInstanceOf(value.object<jobject>(),jclass_of_double)) {
        v = value.callMethod<jdouble>("doubleValue","()D");
    } else if (env->IsInstanceOf(value.object<jobject>(), jclass_of_map)) {
        v = createVariantMap(value.object<jobject>());
    } else if (env->IsInstanceOf(value.object<jobject>(),jclass_of_list)) {
        QVariantList list;
        int count = value.callMethod<jint>("size","()I");
        for (int i = 0 ; i < count ; i++) {
            QAndroidJniObject item = value.callObjectMethod("get","(I)Ljava/lang/Object;",i);
            list.append(convertToQVariant(item));
        }
        v = list;
    } else {
         qWarning() << "value is not an instance of any of the handled jclass types\n";
    }

    env->DeleteLocalRef(jclass_of_string);
    env->DeleteLocalRef(jclass_of_integer);
    env->DeleteLocalRef(jclass_of_long);
    env->DeleteLocalRef(jclass_of_float);
    env->DeleteLocalRef(jclass_of_double);
    env->DeleteLocalRef(jclass_of_boolean);
    env->DeleteLocalRef(jclass_of_list);
    env->DeleteLocalRef(jclass_of_map);

    return v;
}

static QVariantMap createVariantMap(jobject data) {
    QVariantMap res;

    QAndroidJniEnvironment env;
    /* Reference : https://community.oracle.com/thread/1549999 */

    // Get the HashMap Class
    jclass jclass_of_hashmap = (env)->GetObjectClass(data);

    // Get link to Method "entrySet"
    jmethodID entrySetMethod = (env)->GetMethodID(jclass_of_hashmap, "entrySet", "()Ljava/util/Set;");

    // Invoke the "entrySet" method on the HashMap object
    jobject jobject_of_entryset = env->CallObjectMethod(data, entrySetMethod);

    // Get the Set Class
    jclass jclass_of_set = (env)->FindClass("java/util/Set"); // Problem during compilation !!!!!

    if (jclass_of_set == 0) {
         qWarning() << "java/util/Set lookup failed\n";
         return res;
    }

    // Get link to Method "iterator"
    jmethodID iteratorMethod = env->GetMethodID(jclass_of_set, "iterator", "()Ljava/util/Iterator;");

    // Invoke the "iterator" method on the jobject_of_entryset variable of type Set
    jobject jobject_of_iterator = env->CallObjectMethod(jobject_of_entryset, iteratorMethod);

    // Get the "Iterator" class
    jclass jclass_of_iterator = (env)->FindClass("java/util/Iterator");

    // Get link to Method "hasNext"
    jmethodID hasNextMethod = env->GetMethodID(jclass_of_iterator, "hasNext", "()Z");

    jmethodID nextMethod = env->GetMethodID(jclass_of_iterator, "next", "()Ljava/lang/Object;");

    while (env->CallBooleanMethod(jobject_of_iterator, hasNextMethod) ) {
        jobject jEntry = env->CallObjectMethod(jobject_of_iterator,nextMethod);
        QAndroidJniObject entry = QAndroidJniObject(jEntry);
        QAndroidJniObject key = entry.callObjectMethod("getKey","()Ljava/lang/Object;");
        QAndroidJniObject value = entry.callObjectMethod("getValue","()Ljava/lang/Object;");
        QString k = key.toString();

        QVariant v = convertToQVariant(value);

        env->DeleteLocalRef(jEntry);

        if (v.isNull()) {
            continue;
        }

        res[k] = v;
    }

    if (env->ExceptionOccurred()) {
        env->ExceptionDescribe();
        env->ExceptionClear();
    }

    env->DeleteLocalRef(jclass_of_hashmap);
    env->DeleteLocalRef(jobject_of_entryset);
    env->DeleteLocalRef(jclass_of_set);
    env->DeleteLocalRef(jobject_of_iterator);
    env->DeleteLocalRef(jclass_of_iterator);

    return res;
}

static jobject convertToJObject(QVariant v) {
    jobject res = 0;
    QAndroidJniEnvironment env;

    if (v.type() == QVariant::String) {
        QString str = v.toString();
        res = env->NewStringUTF(str.toLocal8Bit().data());
    } else if (v.type() == QVariant::Int) {
        jclass integerClass = env->FindClass("java/lang/Integer");
        jmethodID integerConstructor = env->GetMethodID(integerClass, "<init>", "(I)V");

        res = env->NewObject(integerClass,integerConstructor,v.toInt());

        env->DeleteLocalRef(integerClass);
    } else if (v.type() == QVariant::LongLong) {
        jclass longClass = env->FindClass("java/lang/Long");
        jmethodID longConstructor = env->GetMethodID( longClass, "<init>", "(J)V");

        res = env->NewObject(longClass,longConstructor, v.toLongLong()  );

        env->DeleteLocalRef(longClass);

    } else if (v.type() == QVariant::Double) {
        jclass doubleClass = env->FindClass("java/lang/Double");
        jmethodID doubleConstructor = env->GetMethodID( doubleClass, "<init>", "(D)V");

        res = env->NewObject(doubleClass,doubleConstructor,v.toDouble());

        env->DeleteLocalRef(doubleClass);

    } else if (v.canConvert<float>()) {
        jclass floatClass = env->FindClass("java/lang/Float");
        jmethodID floatConstructor = env->GetMethodID( floatClass, "<init>", "(F)V");

        res = env->NewObject(floatClass,floatConstructor,v.toFloat());

        env->DeleteLocalRef(floatClass);

    } else if (v.type() == QVariant::Bool) {
        jclass booleanClass = env->FindClass("java/lang/Boolean");
        jmethodID booleanConstructor = env->GetMethodID(booleanClass,"<init>","(Z)V");

        res = env->NewObject(booleanClass,booleanConstructor,v.toBool());

        env->DeleteLocalRef(booleanClass);

    } else if (v.type() == QVariant::Map) {
        res = createHashMap(v.toMap());
    } else  if (v.type() == QVariant::List){
        QVariantList list = v.value<QVariantList>();
        jclass arrayListClass = env->FindClass("java/util/ArrayList");
        jmethodID init = env->GetMethodID(arrayListClass, "<init>", "(I)V");
        res = env->NewObject( arrayListClass, init, list.size());

        jmethodID add = env->GetMethodID( arrayListClass, "add",
                    "(Ljava/lang/Object;)Z");

        for (int i = 0 ; i < list.size() ; i++) {
            jobject item = convertToJObject(list.at(i));
            env->CallBooleanMethod(res,add, item);
            env->DeleteLocalRef(item);
        }

        env->DeleteLocalRef(arrayListClass);
    } else {
        qWarning() << "ANSystemDispatcher: Non-supported data type - " <<  v.type();
    }
    return res;
}

static jobject createHashMap(const QVariantMap &data) {
    QAndroidJniEnvironment env;

    jclass mapClass = env->FindClass("java/util/HashMap");

    if (mapClass == NULL)  {
        qWarning() << "Failed to find class" << "java/util/HashMap";
        return NULL;
    }

    jsize map_len = data.size();

    jmethodID init = env->GetMethodID(mapClass, "<init>", "(I)V");
    jobject hashMap = env->NewObject( mapClass, init, map_len);

    jmethodID put = env->GetMethodID( mapClass, "put",
                "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");

    QMapIterator<QString, QVariant> iter(data);
    while (iter.hasNext()) {
        iter.next();

        QString key = iter.key();
        jstring jkey = env->NewStringUTF(key.toLocal8Bit().data());
        QVariant v = iter.value();
        jobject item = convertToJObject(v);

        if (item == 0) {
            continue;
        }

        env->CallObjectMethod(hashMap,put,jkey,item);
        env->DeleteLocalRef(item);
        env->DeleteLocalRef(jkey);
     }

    if (env->ExceptionOccurred()) {
        env->ExceptionDescribe();
        env->ExceptionClear();
    }

    env->DeleteLocalRef(mapClass);

    return hashMap;
}

static void jniEmit(JNIEnv* env,jobject object,jstring name,jobject data) {
    Q_UNUSED(object);
    Q_UNUSED(env);

    QAndroidJniObject tmp(name);
    QString str = tmp.toString();

    QVariantMap map;

    if (data != 0) {
        map = createVariantMap(data);
    }

    if (m_instance.isNull()) {
        return;
    }

    QMetaObject::invokeMethod(m_instance.data(),"dispatched",Qt::AutoConnection,
                              Q_ARG(QString, str),
                              Q_ARG(QVariantMap,map));
}

static void printInstruction() {
    static bool printOnce = false;

    if (printOnce) {
        return;
    }
    printOnce = true;

    // BuildMyString.com generated code. Please enjoy your string responsibly.

    QString msg =  "Using SystemDispatcher in Android but SystemDispatcher::registerNatives() is never called. Instruction: \r\n"
           "https://github.com/benlau/androidnative.pri/blob/master/docs/installation.md\r\n";

   qWarning() << msg;
}
#endif

AndroidNative::SystemDispatcher::SystemDispatcher(QObject* parent) : QObject(parent)
{
}

AndroidNative::SystemDispatcher::~SystemDispatcher()
{
}

AndroidNative::SystemDispatcher *SystemDispatcher::instance()
{
    if (!m_instance) {
        QCoreApplication* app = QCoreApplication::instance();
        m_instance = new SystemDispatcher(app);
    }
    return m_instance;
}

void AndroidNative::SystemDispatcher::dispatch(QString type, QVariantMap message)
{
    Q_UNUSED(type);
    Q_UNUSED(message);

#ifdef Q_OS_ANDROID
    if (!registerNativesCalled) {
        printInstruction();
    }

    QAndroidJniEnvironment env;

    jstring jType = env->NewStringUTF(type.toLocal8Bit().data());
    jobject jData = createHashMap(message);
    QAndroidJniObject::callStaticMethod<void>(JCLASS_Name, "dispatch",
                                              DISPATCH_SIGNATURE,
                                              jType,jData);
    env->DeleteLocalRef(jType);
    env->DeleteLocalRef(jData);

#else
    static bool dispatching = false;
    static QQueue<QPair<QString,QVariantMap> > queue;

    if (dispatching) {
        queue.enqueue(QPair<QString,QVariantMap> (type,message) );
        return;
    }

    dispatching = true;
    emit dispatched(type,message);

    while (queue.size() > 0) {
        QPair<QString,QVariantMap> pair = queue.dequeue();
        emit dispatched(pair.first,pair.second);
    }
    dispatching = false;
#endif
}

void AndroidNative::SystemDispatcher::loadClass(QString javaClassName)
{
    QVariantMap message;
    message["className"] = javaClassName;

    dispatch("androidnative.SystemDispatcher.loadClass",message);
}

void AndroidNative::SystemDispatcher::registerNatives()
{
    Q_UNUSED(registerNativesCalled);

#ifdef Q_OS_ANDROID
    registerNativesCalled = true;

    QAndroidJniEnvironment env;
    jclass clazz = env->FindClass(JCLASS_Name);
    if (!clazz)
    {
        qCritical() <<  "androidNative.SystemDispatcher Java class is not found. Please configure your build.gradle according to this instruction: \r\n"
                        "https://github.com/benlau/androidnative.pri/blob/master/docs/installation.md\r\n";
        if (env->ExceptionOccurred()) {
            env->ExceptionDescribe();
            env->ExceptionClear();
        }
        return;
    }

    JNINativeMethod methods[] =
    {
        {"jniEmit", EMIT_SIGNATURE, (void *)&jniEmit},
    };

    int numMethods = sizeof(methods) / sizeof(methods[0]);
    if (env->RegisterNatives(clazz, methods, numMethods) < 0) {
        if (env->ExceptionOccurred()) {
            env->ExceptionDescribe();
            env->ExceptionClear();
            qCritical() << "Exception occurred!!!";
            return;
        }
    }

    QAndroidJniObject::callStaticMethod<void>(JCLASS_Name, "init",
                                              "()V");
#endif
}
<|endoftext|>


<commit_before>/** \file    populate_in_tuebingen_available.cc
 *  \author  Dr. Johannes Ruscheinski
 *
 *  A tool that adds a new "SIG" field to a MARC record if there are UB or IFK call numbers in a record.
 */

/*
    Copyright (C) 2015,2016, Library of the University of Tübingen

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <cstdlib>
#include "DirectoryEntry.h"
#include "Leader.h"
#include "MarcUtil.h"
#include "MarcXmlWriter.h"
#include "StringUtil.h"
#include "Subfields.h"
#include "util.h"

void Usage() {
    std::cerr << "Usage: " << progname << " [--verbose] marc_input marc_output\n";
    std::exit(EXIT_FAILURE);
}

static unsigned modified_record_count;
static unsigned add_sig_count;

bool ProcessRecord(MarcUtil::Record * const record, XmlWriter * const xml_writer, std::string * const /*err_msg*/) {
    std::vector <std::pair<size_t, size_t>> local_block_boundaries;
    record->findAllLocalDataBlocks(&local_block_boundaries);

    bool modified_record(false);
    const std::vector<std::string> &fields(record->getFields());
    for (const auto &block_start_and_end : local_block_boundaries) {
        std::vector <size_t> _852_field_indices;
        if (record->findFieldsInLocalBlock("852", "??", block_start_and_end, &_852_field_indices) == 0)
            continue;
        for (const size_t _852_index : _852_field_indices) {
            const Subfields subfields1(fields[_852_index]);
            const std::string not_available_subfield(subfields1.getFirstSubfieldValue('z'));
            if (not_available_subfield == "Kein Bestand am IfK; Nachweis für KrimDok") {
                record->filterTags({"SIG"});
                record->write(xml_writer);
                return true;
            }

            const std::string isil_subfield(subfields1.getFirstSubfieldValue('a'));
            if (isil_subfield != "DE-21" and isil_subfield != "DE-21-110")
                continue;

            std::string detailed_availability;
            std::vector <size_t> _866_field_indices;
            if (record->findFieldsInLocalBlock("866", "30", block_start_and_end, &_866_field_indices) > 0) {
                for (const size_t _866_index : _866_field_indices) {
                    const Subfields subfields2(fields[_866_index]);
                    const std::string subfield_a(subfields2.getFirstSubfieldValue('a'));
                    if (not subfield_a.empty()) {
                        if (not detailed_availability.empty())
                            detailed_availability += "; ";
                        detailed_availability += subfield_a;
                        const std::string subfield_z(subfields2.getFirstSubfieldValue('z'));
                        if (not subfield_z.empty())
                            detailed_availability += " " + subfield_z;
                    }
                }
            }

            const std::string institution(isil_subfield == "DE-21" ? "UB: " : "IFK: ");
            if (_852_index + 1 < block_start_and_end.second) {
                const Subfields subfields2(fields[_852_index + 1]);
                const std::string call_number_subfield(subfields2.getFirstSubfieldValue('c'));
                if (not call_number_subfield.empty()) {
                    const std::string institution_and_call_number(institution + call_number_subfield);
                    ++add_sig_count;
                    modified_record = true;
                    record->insertField("SIG",
                                        "  ""\x1F""a" + institution_and_call_number
                                        + (detailed_availability.empty() ? "" : "(" + detailed_availability + ")"));
                }
            }
            break;
        }
    }

    if (modified_record)
        ++modified_record_count;

    record->write(xml_writer);
    return true;
}

void PopulateTheInTuebingenAvailableField(const bool verbose, File * const input, File * const output) {
    MarcXmlWriter xml_writer(output);

    std::string err_msg;
    if (not MarcUtil::ProcessRecords(input, ProcessRecord, &xml_writer , &err_msg))
        Error("error while processing records: " + err_msg);

    if (verbose) {
        std::cout << "Modified " << modified_record_count << " records.\n";
        std::cout << "Added " << add_sig_count << " signature fields.\n";
    }
}

int main(int argc, char **argv) {
    progname = argv[0];

    if (argc != 3 and argc != 4)
        Usage();

    bool verbose;
    if (argc == 3)
        verbose = false;
    else { // argc == 4
        if (std::strcmp(argv[1], "--verbose") != 0)
            Usage();
        verbose = true;
    }

    const std::string marc_input_filename(argv[argc == 3 ? 1 : 2]);
    File marc_input(marc_input_filename, "r");
    if (not marc_input)
        Error("can't open \"" + marc_input_filename + "\" for reading!");

    const std::string marc_output_filename(argv[argc == 3 ? 2 : 3]);
    File marc_output(marc_output_filename, "w");
    if (not marc_output)
        Error("can't open \"" + marc_output_filename + "\" for writing!");

    PopulateTheInTuebingenAvailableField(verbose, &marc_input, &marc_output);
}
<commit_msg>Fixed two "In Tübingen available" bugs.<commit_after>/** \file    populate_in_tuebingen_available.cc
 *  \author  Dr. Johannes Ruscheinski
 *
 *  A tool that adds a new "SIG" field to a MARC record if there are UB or IFK call numbers in a record.
 */

/*
    Copyright (C) 2015,2016, Library of the University of Tübingen

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <set>
#include <cstdlib>
#include "DirectoryEntry.h"
#include "HtmlUtil.h"
#include "Leader.h"
#include "MarcUtil.h"
#include "MarcXmlWriter.h"
#include "StringUtil.h"
#include "Subfields.h"
#include "util.h"

void Usage() {
    std::cerr << "Usage: " << progname << " [--verbose] marc_input marc_output\n";
    std::exit(EXIT_FAILURE);
}

static unsigned modified_record_count;
static unsigned add_sig_count;

bool ProcessRecord(MarcUtil::Record * const record, XmlWriter * const xml_writer, std::string * const /*err_msg*/) {
    record->setRecordWillBeWrittenAsXml(true);

    std::vector <std::pair<size_t, size_t>> local_block_boundaries;
    record->findAllLocalDataBlocks(&local_block_boundaries);

    bool modified_record(false);
    const std::vector<std::string> &fields(record->getFields());
    std::set<std::string> alread_seen_urls;
    for (const auto &block_start_and_end : local_block_boundaries) {
        std::vector <size_t> _852_field_indices;
        if (record->findFieldsInLocalBlock("852", "??", block_start_and_end, &_852_field_indices) == 0)
            continue;
        for (const size_t _852_index : _852_field_indices) {
            const Subfields subfields1(fields[_852_index]);
            const std::string not_available_subfield(subfields1.getFirstSubfieldValue('z'));
            if (not_available_subfield == "Kein Bestand am IfK; Nachweis für KrimDok")
                continue;

            const std::string isil_subfield(subfields1.getFirstSubfieldValue('a'));
            if (isil_subfield != "DE-21" and isil_subfield != "DE-21-110")
                continue;

            std::string detailed_availability;
            std::vector <size_t> _866_field_indices;
            if (record->findFieldsInLocalBlock("866", "30", block_start_and_end, &_866_field_indices) > 0) {
                for (const size_t _866_index : _866_field_indices) {
                    const Subfields subfields2(fields[_866_index]);
                    const std::string subfield_a(subfields2.getFirstSubfieldValue('a'));
                    if (not subfield_a.empty()) {
                        if (not detailed_availability.empty())
                            detailed_availability += "; ";
                        detailed_availability += subfield_a;
                        const std::string subfield_z(subfields2.getFirstSubfieldValue('z'));
                        if (not subfield_z.empty())
                            detailed_availability += " " + subfield_z;
                    }
                }
            }

            const std::string institution(isil_subfield == "DE-21" ? "UB: " : "IFK: ");
            if (_852_index + 1 < block_start_and_end.second) {
                const Subfields subfields2(fields[_852_index + 1]);
                const std::string call_number_subfield(subfields2.getFirstSubfieldValue('c'));
                if (not call_number_subfield.empty()) {
                    const std::string institution_and_call_number(institution + call_number_subfield);
                    ++add_sig_count;
                    modified_record = true;
                    record->insertField("SIG",
                                        "  ""\x1F""a" + institution_and_call_number
                                        + (detailed_availability.empty() ? "" : "(" + detailed_availability + ")"));
                } else { // Look for a URL.
                    std::vector <size_t> _856_field_indices;
                    if (record->findFieldsInLocalBlock("856", "4 ", block_start_and_end, &_856_field_indices) > 0) {
                        const Subfields subfields3(fields[_856_field_indices.front()]);
                        if (subfields3.hasSubfield('u')) {
                            const std::string url(subfields3.getFirstSubfieldValue('u'));
                            if (alread_seen_urls.find(url) == alread_seen_urls.cend()) {
                                alread_seen_urls.insert(url);
                                std::string anchor(HtmlUtil::HtmlEscape(subfields3.getFirstSubfieldValue('x')));
                                if (anchor.empty())
                                    anchor = "Tübingen Online Resource";
                                record->insertField("SIG", "\x1F""a" + anchor + "");
                            }
                        }
                    }
                }
            }
            break;
        }
    }

    if (modified_record)
        ++modified_record_count;

    record->write(xml_writer);
    return true;
}

void PopulateTheInTuebingenAvailableField(const bool verbose, File * const input, File * const output) {
    MarcXmlWriter xml_writer(output);

    std::string err_msg;
    if (not MarcUtil::ProcessRecords(input, ProcessRecord, &xml_writer , &err_msg))
        Error("error while processing records: " + err_msg);

    if (verbose) {
        std::cout << "Modified " << modified_record_count << " records.\n";
        std::cout << "Added " << add_sig_count << " signature fields.\n";
    }
}

int main(int argc, char **argv) {
    progname = argv[0];

    if (argc != 3 and argc != 4)
        Usage();

    bool verbose;
    if (argc == 3)
        verbose = false;
    else { // argc == 4
        if (std::strcmp(argv[1], "--verbose") != 0)
            Usage();
        verbose = true;
    }

    const std::string marc_input_filename(argv[argc == 3 ? 1 : 2]);
    File marc_input(marc_input_filename, "r");
    if (not marc_input)
        Error("can't open \"" + marc_input_filename + "\" for reading!");

    const std::string marc_output_filename(argv[argc == 3 ? 2 : 3]);
    File marc_output(marc_output_filename, "w");
    if (not marc_output)
        Error("can't open \"" + marc_output_filename + "\" for writing!");

    PopulateTheInTuebingenAvailableField(verbose, &marc_input, &marc_output);
}
<|endoftext|>