diff src/name/blackcap/exifwasher/exiv2/native.cpp @ 13:a59d84674fb0

Make it seamlessly work on IPTC and XMP metadata, too.
author David Barts <davidb@stashtea.com>
date Sat, 11 Apr 2020 09:14:31 -0700
parents efd9fe2d70d7
children cd2ca4727b7f
line wrap: on
line diff
--- a/src/name/blackcap/exifwasher/exiv2/native.cpp	Fri Apr 10 23:22:36 2020 -0700
+++ b/src/name/blackcap/exifwasher/exiv2/native.cpp	Sat Apr 11 09:14:31 2020 -0700
@@ -1,4 +1,5 @@
 #include <jni.h>
+#include <string.h>
 #include <exiv2/exiv2.hpp>
 #include <exception>
 #include <iostream>
@@ -16,8 +17,10 @@
 /*
  * Utility function to get pointer field.
  */
-jlong getPointer(JNIEnv *jEnv, jobject jThis) {
-    return jEnv->GetLongField(jThis, jEnv->GetFieldID(jEnv->GetObjectClass(jThis), "pointer", "J"));
+Exiv2::Image *getPointer(JNIEnv *jEnv, jobject jThis) {
+    return reinterpret_cast<Exiv2::Image *>
+        (jEnv->GetLongField(jThis,
+            jEnv->GetFieldID(jEnv->GetObjectClass(jThis), "pointer", "J")));
 }
 
 /*
@@ -47,7 +50,7 @@
  */
 JNIEXPORT void JNICALL Java_name_blackcap_exifwasher_exiv2_Image__1writeMetadata
   (JNIEnv *jEnv, jobject jThis) {
-      Exiv2::Image *image = reinterpret_cast<Exiv2::Image *> (getPointer(jEnv, jThis));
+      Exiv2::Image *image = getPointer(jEnv, jThis);
       if (jEnv->ExceptionCheck()) return;
       try {
           image->writeMetadata();
@@ -65,7 +68,7 @@
  */
 JNIEXPORT jlong JNICALL Java_name_blackcap_exifwasher_exiv2_Image__1getMetadata
   (JNIEnv *jEnv, jobject jThis) {
-      Exiv2::Image *image = reinterpret_cast<Exiv2::Image *> (getPointer(jEnv, jThis));
+      Exiv2::Image *image = getPointer(jEnv, jThis);
       if (jEnv->ExceptionCheck()) return 0;
       try {
           image->readMetadata();
@@ -75,7 +78,7 @@
           jEnv->ThrowNew(ex, e.what());
           return 0;
       }
-      return reinterpret_cast<jlong> (&(image->exifData()));
+      return reinterpret_cast<jlong>(image);
   }
 
 /*
@@ -85,7 +88,7 @@
  */
 JNIEXPORT void JNICALL Java_name_blackcap_exifwasher_exiv2_Image__1dtor
   (JNIEnv *jEnv, jobject jThis) {
-      Exiv2::Image *image = reinterpret_cast<Exiv2::Image *> (getPointer(jEnv, jThis));
+      Exiv2::Image *image = getPointer(jEnv, jThis);
       if (jEnv->ExceptionCheck()) return;
       delete image;
   }
@@ -96,11 +99,68 @@
 #endif
 /* Header for class name_blackcap_exifwasher_exiv2_ExifData */
 
+/*
+ * Utility function to delete a (key, value) pair.
+ */
+template<class D, class K>
+void deleteValue(D &data, const char *key) {
+    typename D::iterator found = data.findKey(K(std::string(key)));
+    data.erase(found);
+}
+
+/*
+ * Utility function to retrieve a value, given a key.
+ */
+template<class D, class K>
+jobject getValue(JNIEnv *jEnv, D &data, const char *key) {
+    typename D::iterator found = data.findKey(K(std::string(key)));
+    if (found == data.end()) {
+        return NULL;
+    }
+    jclass klass = jEnv->FindClass("name/blackcap/exifwasher/exiv2/ExifData$Value");
+    if (jEnv->ExceptionCheck()) return NULL;
+    jstring type = jEnv->NewStringUTF(found->typeName());
+    jstring value = jEnv->NewStringUTF(found->toString().c_str());
+    jmethodID method = jEnv->GetMethodID(klass, "<init>", "(Ljava/lang/String;Ljava/lang/String;)V");
+    if (jEnv->ExceptionCheck()) return NULL;
+    return jEnv->NewObject(klass, method, type, value);
+}
+
+/*
+ * Utility function to read keys, given a target to put them and a collection
+ * of data.
+ */
+template<class D>
+jsize getKeys(JNIEnv *jEnv, jobjectArray target, jsize start, D &data) {
+    typename D::const_iterator end = data.end();
+    jsize j = start;
+    for (typename D::const_iterator i = data.begin(); i != end; ++i) {
+        jEnv->SetObjectArrayElement(target, j++, jEnv->NewStringUTF(i->key().c_str()));
+        if (jEnv->ExceptionCheck()) break;
+    }
+    return j;
+}
+
 #ifndef _Included_name_blackcap_exifwasher_exiv2_ExifData
 #define _Included_name_blackcap_exifwasher_exiv2_ExifData
 #ifdef __cplusplus
 extern "C" {
 #endif
+
+/*
+ * Utility function to parse out a type prefix from a key name.
+ */
+char *getType(const char *key) {
+    char *dot = strchr(key, '.');
+    if (dot == NULL)
+        throw std::invalid_argument(std::string("invalid key: ") + std::string(key));
+    int length = dot - key;
+    char *ret = (char *) malloc(length + 1);
+    memcpy(ret, key, length);
+    ret[length] = '\0';
+    return ret;
+}
+
 /*
  * Class:     name_blackcap_exifwasher_exiv2_ExifData
  * Method:    _erase
@@ -108,17 +168,27 @@
  */
 JNIEXPORT void JNICALL Java_name_blackcap_exifwasher_exiv2_ExifData__1erase
   (JNIEnv *jEnv, jobject jThis, jstring key) {
-      Exiv2::ExifData *data = reinterpret_cast<Exiv2::ExifData *> (getPointer(jEnv, jThis));
+      Exiv2::Image *image = getPointer(jEnv, jThis);
       if (jEnv->ExceptionCheck()) return;
       const char *cKey = jEnv->GetStringUTFChars(key, NULL);
-      Exiv2::ExifData::iterator found = data->findKey(Exiv2::ExifKey(std::string(cKey)));
+      char *type = NULL;
       try {
-          data->erase(found);
+          type = getType(cKey);
+          if (!strcmp(type, "Exif")) {
+              deleteValue<Exiv2::ExifData, Exiv2::ExifKey>(image->exifData(), cKey);
+          } else if (!strcmp(type, "Xmp")) {
+              deleteValue<Exiv2::XmpData, Exiv2::XmpKey>(image->xmpData(), cKey);
+          } else if (!strcmp(type, "Iptc")) {
+              deleteValue<Exiv2::IptcData, Exiv2::IptcKey>(image->iptcData(), cKey);
+          } else {
+              throw std::invalid_argument(std::string("invalid key: ") + std::string(cKey));
+          }
       } catch (std::exception& e) {
           jEnv->ExceptionClear();
           jclass ex = jEnv->FindClass("name/blackcap/exifwasher/exiv2/Exiv2Exception");
           jEnv->ThrowNew(ex, e.what());
       }
+      if (type != NULL) free(type);
       jEnv->ReleaseStringUTFChars(key, cKey);
   }
 
@@ -129,21 +199,30 @@
  */
 JNIEXPORT jobject JNICALL Java_name_blackcap_exifwasher_exiv2_ExifData__1value
   (JNIEnv *jEnv, jobject jThis, jstring key) {
-      Exiv2::ExifData *data = reinterpret_cast<Exiv2::ExifData *> (getPointer(jEnv, jThis));
+      Exiv2::Image *image = getPointer(jEnv, jThis);
       if (jEnv->ExceptionCheck()) return NULL;
       const char *cKey = jEnv->GetStringUTFChars(key, NULL);
-      Exiv2::ExifData::const_iterator found = data->findKey(Exiv2::ExifKey(std::string(cKey)));
-      jEnv->ReleaseStringUTFChars(key, cKey);
-      if (found == data->end()) {
-          return NULL;
+      char *type = NULL;
+      jobject ret = NULL;
+      try {
+          type = getType(cKey);
+          if (!strcmp(type, "Exif")) {
+              ret = getValue<Exiv2::ExifData, Exiv2::ExifKey>(jEnv, image->exifData(), cKey);
+          } else if (!strcmp(type, "Xmp")) {
+              ret = getValue<Exiv2::XmpData, Exiv2::XmpKey>(jEnv, image->xmpData(), cKey);
+          } else if (!strcmp(type, "Iptc")) {
+              ret = getValue<Exiv2::IptcData, Exiv2::IptcKey>(jEnv, image->iptcData(), cKey);
+          } else {
+              throw std::invalid_argument(std::string("invalid key: ") + std::string(cKey));
+          }
+      } catch (std::exception& e) {
+          jEnv->ExceptionClear();
+          jclass ex = jEnv->FindClass("name/blackcap/exifwasher/exiv2/Exiv2Exception");
+          jEnv->ThrowNew(ex, e.what());
       }
-      jclass klass = jEnv->FindClass("name/blackcap/exifwasher/exiv2/ExifData$Value");
-      if (jEnv->ExceptionCheck()) return NULL;
-      jstring type = jEnv->NewStringUTF(found->typeName());
-      jstring value = jEnv->NewStringUTF(found->toString().c_str());
-      jmethodID method = jEnv->GetMethodID(klass, "<init>", "(Ljava/lang/String;Ljava/lang/String;)V");
-      if (jEnv->ExceptionCheck()) return NULL;
-      return jEnv->NewObject(klass, method, type, value);
+      if (type != NULL) free(type);
+      jEnv->ReleaseStringUTFChars(key, cKey);
+      return ret;
   }
 
 /*
@@ -153,18 +232,21 @@
  */
 JNIEXPORT jobjectArray JNICALL Java_name_blackcap_exifwasher_exiv2_ExifData__1keys
   (JNIEnv *jEnv, jobject jThis) {
-      Exiv2::ExifData *data = reinterpret_cast<Exiv2::ExifData *> (getPointer(jEnv, jThis));
+      Exiv2::Image *image = getPointer(jEnv, jThis);
+      Exiv2::ExifData eData = image->exifData();
+      Exiv2::XmpData xData = image->xmpData();
+      Exiv2::IptcData iData = image->iptcData();
       if (jEnv->ExceptionCheck()) return NULL;
       jclass klass = jEnv->FindClass("java/lang/String");
       if (jEnv->ExceptionCheck()) return NULL;
-      jobjectArray ret = jEnv->NewObjectArray(data->count(), klass, NULL);
+      jobjectArray ret = jEnv->NewObjectArray(eData.count() + iData.count() + xData.count(), klass, NULL);
+      if (jEnv->ExceptionCheck()) return NULL;
+      jsize start = getKeys(jEnv, ret, 0, eData);
       if (jEnv->ExceptionCheck()) return NULL;
-      Exiv2::ExifData::const_iterator end = data->end();
-      jsize j = 0;
-      for (Exiv2::ExifData::const_iterator i = data->begin(); i != end; ++i) {
-          jEnv->SetObjectArrayElement(ret, j++, jEnv->NewStringUTF(i->key().c_str()));
-          if (jEnv->ExceptionCheck()) break;
-      }
+      start = getKeys(jEnv, ret, start, xData);
+      if (jEnv->ExceptionCheck()) return NULL;
+      start = getKeys(jEnv, ret, start, iData);
+      if (jEnv->ExceptionCheck()) return NULL;
       return ret;
   }