Refactor java dependencies
This commit is contained in:
parent
b57ef8b0b5
commit
f8e6288b63
7 changed files with 380 additions and 357 deletions
|
@ -2,7 +2,6 @@
|
|||
#include <vector>
|
||||
#include <SkPath.h>
|
||||
#include <SkBitmap.h>
|
||||
#include <jni.h>
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
|
||||
|
|
|
@ -60,6 +60,14 @@ namespace __gnu_cxx {
|
|||
size_t
|
||||
operator()(unsigned long long int __x) const
|
||||
{ return __x; }
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<void*>
|
||||
{
|
||||
size_t
|
||||
operator()(void* __x) const
|
||||
{ return (size_t) __x; }
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -70,6 +78,8 @@ namespace __gnu_cxx {
|
|||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
|
|
306
Osmand-kernel/osmand/src/java_renderRules.h
Normal file
306
Osmand-kernel/osmand/src/java_renderRules.h
Normal file
|
@ -0,0 +1,306 @@
|
|||
#ifndef _JAVA_RENDER_RULES_H
|
||||
#define _JAVA_RENDER_RULES_H
|
||||
|
||||
#include <jni.h>
|
||||
#include "java_wrap.h"
|
||||
#include "common.h"
|
||||
#include "renderRules.h"
|
||||
|
||||
|
||||
jclass ListClass;
|
||||
jmethodID List_size;
|
||||
jmethodID List_get;
|
||||
|
||||
jclass RenderingRuleClass;
|
||||
jfieldID RenderingRule_properties;
|
||||
jfieldID RenderingRule_intProperties;
|
||||
jfieldID RenderingRule_floatProperties;
|
||||
jfieldID RenderingRule_ifElseChildren;
|
||||
jfieldID RenderingRule_ifChildren;
|
||||
|
||||
jclass RenderingRuleStoragePropertiesClass;
|
||||
jfieldID RenderingRuleStorageProperties_rules;
|
||||
|
||||
jclass RenderingRulePropertyClass;
|
||||
jfieldID RenderingRuleProperty_type;
|
||||
jfieldID RenderingRuleProperty_input;
|
||||
jfieldID RenderingRuleProperty_attrName;
|
||||
|
||||
jclass RenderingRulesStorageClass;
|
||||
jfieldID RenderingRulesStorageClass_dictionary;
|
||||
jfieldID RenderingRulesStorage_PROPS;
|
||||
jmethodID RenderingRulesStorage_getRules;
|
||||
|
||||
jclass RenderingRuleSearchRequestClass;
|
||||
jfieldID RenderingRuleSearchRequest_storage;
|
||||
jfieldID RenderingRuleSearchRequest_props;
|
||||
jfieldID RenderingRuleSearchRequest_values;
|
||||
jfieldID RenderingRuleSearchRequest_fvalues;
|
||||
jfieldID RenderingRuleSearchRequest_savedValues;
|
||||
jfieldID RenderingRuleSearchRequest_savedFvalues;
|
||||
|
||||
RenderingRule createRenderingRule(JNIEnv* env, jobject rRule, RenderingRulesStorage* st) {
|
||||
RenderingRule rule;
|
||||
jobjectArray props = (jobjectArray) env->GetObjectField(rRule, RenderingRule_properties);
|
||||
jintArray intProps = (jintArray) env->GetObjectField(rRule, RenderingRule_intProperties);
|
||||
jfloatArray floatProps = (jfloatArray) env->GetObjectField(rRule, RenderingRule_floatProperties);
|
||||
jobject ifChildren = env->GetObjectField(rRule, RenderingRule_ifChildren);
|
||||
jobject ifElseChildren = env->GetObjectField(rRule, RenderingRule_ifElseChildren);
|
||||
|
||||
jsize sz = env->GetArrayLength(props);
|
||||
|
||||
if (floatProps != NULL) {
|
||||
jfloat* fe = env->GetFloatArrayElements(floatProps, NULL);
|
||||
for (int j = 0; j < sz; j++) {
|
||||
rule.floatProperties.push_back(fe[j]);
|
||||
}
|
||||
env->ReleaseFloatArrayElements(floatProps, fe, JNI_ABORT);
|
||||
env->DeleteLocalRef(floatProps);
|
||||
} else {
|
||||
rule.floatProperties.assign(sz, 0);
|
||||
}
|
||||
|
||||
if (intProps != NULL) {
|
||||
jint* ie = env->GetIntArrayElements(intProps, NULL);
|
||||
for (int j = 0; j < sz; j++) {
|
||||
rule.intProperties.push_back(ie[j]);
|
||||
}
|
||||
env->ReleaseIntArrayElements(intProps, ie, JNI_ABORT);
|
||||
env->DeleteLocalRef(intProps);
|
||||
} else {
|
||||
rule.intProperties.assign(sz, -1);
|
||||
}
|
||||
|
||||
for (jsize i = 0; i < sz; i++) {
|
||||
jobject prop = env->GetObjectArrayElement(props, i);
|
||||
std::string attr = getStringField(env, prop, RenderingRuleProperty_attrName);
|
||||
RenderingRuleProperty* p = st->getProperty(attr.c_str());
|
||||
rule.properties.push_back(p);
|
||||
env->DeleteLocalRef(prop);
|
||||
}
|
||||
env->DeleteLocalRef(props);
|
||||
|
||||
if (ifChildren != NULL) {
|
||||
sz = env->CallIntMethod(ifChildren, List_size);
|
||||
for (jsize i = 0; i < sz; i++) {
|
||||
jobject o = env->CallObjectMethod(ifChildren, List_get, i);
|
||||
rule.ifChildren.push_back(createRenderingRule(env, o, st));
|
||||
env->DeleteLocalRef(o);
|
||||
}
|
||||
env->DeleteLocalRef(ifChildren);
|
||||
}
|
||||
|
||||
if (ifElseChildren != NULL) {
|
||||
sz = env->CallIntMethod(ifElseChildren, List_size);
|
||||
for (jsize i = 0; i < sz; i++) {
|
||||
jobject o = env->CallObjectMethod(ifElseChildren, List_get, i);
|
||||
rule.ifElseChildren.push_back(createRenderingRule(env, o, st));
|
||||
env->DeleteLocalRef(o);
|
||||
}
|
||||
env->DeleteLocalRef(ifElseChildren);
|
||||
}
|
||||
|
||||
return rule;
|
||||
}
|
||||
|
||||
|
||||
void initDictionary(JNIEnv* env, RenderingRulesStorage* storage, jobject javaStorage) {
|
||||
jobject listDictionary = env->GetObjectField(javaStorage, RenderingRulesStorageClass_dictionary);
|
||||
uint sz = env->CallIntMethod(listDictionary, List_size);
|
||||
uint i = 0;
|
||||
for (; i < sz; i++) {
|
||||
jstring st = (jstring) env->CallObjectMethod(listDictionary, List_get, i);
|
||||
// if(st != NULL)
|
||||
// {
|
||||
const char* utf = env->GetStringUTFChars(st, NULL);
|
||||
std::string d = std::string(utf);
|
||||
|
||||
env->ReleaseStringUTFChars(st, utf);
|
||||
env->DeleteLocalRef(st);
|
||||
storage->dictionary.push_back(d);
|
||||
storage->dictionaryMap[d] = i;
|
||||
// }
|
||||
}
|
||||
env->DeleteLocalRef(listDictionary);
|
||||
}
|
||||
|
||||
void initProperties(JNIEnv* env, RenderingRulesStorage* st, jobject javaStorage) {
|
||||
jobject props = env->GetObjectField(javaStorage, RenderingRulesStorage_PROPS);
|
||||
jobject listProps = env->GetObjectField(props, RenderingRuleStorageProperties_rules);
|
||||
uint sz = env->CallIntMethod(listProps, List_size);
|
||||
uint i = 0;
|
||||
for (; i < sz; i++) {
|
||||
jobject rulePrope = env->CallObjectMethod(listProps, List_get, i);
|
||||
bool input = (env->GetBooleanField(rulePrope, RenderingRuleProperty_input) == JNI_TRUE);
|
||||
int type = env->GetIntField(rulePrope, RenderingRuleProperty_type);
|
||||
std::string name = getStringField(env, rulePrope, RenderingRuleProperty_attrName);
|
||||
RenderingRuleProperty* prop = new RenderingRuleProperty(type, input, name, i);
|
||||
st->properties.push_back(*prop);
|
||||
st->propertyMap[name] = prop;
|
||||
env->DeleteLocalRef(rulePrope);
|
||||
}
|
||||
env->DeleteLocalRef(props);
|
||||
env->DeleteLocalRef(listProps);
|
||||
|
||||
}
|
||||
|
||||
void initRules(JNIEnv* env, RenderingRulesStorage* st, jobject javaStorage) {
|
||||
for (int i = 1; i < st->SIZE_STATES; i++) {
|
||||
jobjectArray rules = (jobjectArray) env->CallObjectMethod(javaStorage, RenderingRulesStorage_getRules,
|
||||
i);
|
||||
jsize len = env->GetArrayLength(rules);
|
||||
for (jsize j = 0; j < len; j++) {
|
||||
jobject rRule = env->GetObjectArrayElement(rules, j);
|
||||
RenderingRule rule = createRenderingRule(env, rRule, st);
|
||||
env->DeleteLocalRef(rRule);
|
||||
|
||||
jsize psz = rule.properties.size();
|
||||
int tag = -1;
|
||||
int value = -1;
|
||||
for (int p = 0; p < psz; p++) {
|
||||
if (rule.properties.at(p)->attrName == "tag") {
|
||||
tag = rule.intProperties.at(p);
|
||||
} else if (rule.properties.at(p)->attrName == "value") {
|
||||
value = rule.intProperties.at(p);
|
||||
}
|
||||
}
|
||||
if (tag != -1 && value != -1) {
|
||||
int key = (tag << st->SHIFT_TAG_VAL) + value;
|
||||
st->tagValueGlobalRules[i][key] = rule;
|
||||
}
|
||||
}
|
||||
env->DeleteLocalRef(rules);
|
||||
}
|
||||
}
|
||||
|
||||
RenderingRulesStorage* createRenderingRulesStorage(JNIEnv* env, jobject storage) {
|
||||
RenderingRulesStorage* res = new RenderingRulesStorage(storage);
|
||||
initDictionary(env, res, storage);
|
||||
initProperties(env, res, storage);
|
||||
initRules(env, res, storage);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void initRenderingRuleSearchRequest(JNIEnv* env, RenderingRuleSearchRequest* r, jobject rrs) {
|
||||
jsize sz;
|
||||
jobjectArray oa = (jobjectArray) env->GetObjectField(rrs, RenderingRuleSearchRequest_props);
|
||||
sz = env->GetArrayLength(oa);
|
||||
std::vector<RenderingRuleProperty*> requestProps;
|
||||
int* values;
|
||||
float* fvalues;
|
||||
int* savedValues;
|
||||
float* savedFvalues;
|
||||
|
||||
for (jsize i = 0; i < sz; i++) {
|
||||
jobject prop = env->GetObjectArrayElement(oa, i);
|
||||
std::string attr = getStringField(env, prop, RenderingRuleProperty_attrName);
|
||||
RenderingRuleProperty* p = r->storage->getProperty(attr.c_str());
|
||||
requestProps.push_back(p);
|
||||
env->DeleteLocalRef(prop);
|
||||
}
|
||||
env->DeleteLocalRef(oa);
|
||||
sz = r->storage->getPropertiesSize();
|
||||
{
|
||||
values = new int[sz];
|
||||
jintArray ia = (jintArray) env->GetObjectField(rrs, RenderingRuleSearchRequest_values);
|
||||
jint* ie = env->GetIntArrayElements(ia, NULL);
|
||||
for (int i = 0; i < sz; i++) {
|
||||
values[requestProps.at(i)->id] = ie[i];
|
||||
}
|
||||
env->ReleaseIntArrayElements(ia, ie, JNI_ABORT);
|
||||
env->DeleteLocalRef(ia);
|
||||
}
|
||||
|
||||
{
|
||||
fvalues = new float[sz];
|
||||
jfloatArray ia = (jfloatArray) env->GetObjectField(rrs, RenderingRuleSearchRequest_fvalues);
|
||||
jfloat* ie = env->GetFloatArrayElements(ia, NULL);
|
||||
for (int i = 0; i < sz; i++) {
|
||||
fvalues[requestProps.at(i)->id] = ie[i];
|
||||
}
|
||||
env->ReleaseFloatArrayElements(ia, ie, JNI_ABORT);
|
||||
env->DeleteLocalRef(ia);
|
||||
}
|
||||
|
||||
{
|
||||
savedValues = new int[sz];
|
||||
jintArray ia = (jintArray) env->GetObjectField(rrs, RenderingRuleSearchRequest_values);
|
||||
jint* ie = env->GetIntArrayElements(ia, NULL);
|
||||
for (int i = 0; i < sz; i++) {
|
||||
savedValues[requestProps.at(i)->id] = ie[i];
|
||||
}
|
||||
env->ReleaseIntArrayElements(ia, ie, JNI_ABORT);
|
||||
env->DeleteLocalRef(ia);
|
||||
}
|
||||
|
||||
{
|
||||
savedFvalues = new float[sz];
|
||||
jfloatArray ia = (jfloatArray) env->GetObjectField(rrs, RenderingRuleSearchRequest_fvalues);
|
||||
jfloat* ie = env->GetFloatArrayElements(ia, NULL);
|
||||
for (int i = 0; i < sz; i++) {
|
||||
savedFvalues[requestProps.at(i)->id] = ie[i];
|
||||
}
|
||||
env->ReleaseFloatArrayElements(ia, ie, JNI_ABORT);
|
||||
env->DeleteLocalRef(ia);
|
||||
}
|
||||
r->externalInitialize(values, fvalues, savedValues, savedFvalues);
|
||||
}
|
||||
|
||||
|
||||
void loadJniRenderingRules(JNIEnv* env) {
|
||||
RenderingRuleClass = findClass(env, "net/osmand/render/RenderingRule");
|
||||
RenderingRule_properties = env->GetFieldID(RenderingRuleClass, "properties",
|
||||
"[Lnet/osmand/render/RenderingRuleProperty;");
|
||||
RenderingRule_intProperties = env->GetFieldID(RenderingRuleClass, "intProperties", "[I");
|
||||
RenderingRule_floatProperties = env->GetFieldID(RenderingRuleClass, "floatProperties", "[F");
|
||||
RenderingRule_ifElseChildren = env->GetFieldID(RenderingRuleClass, "ifElseChildren", "Ljava/util/List;");
|
||||
RenderingRule_ifChildren = env->GetFieldID(RenderingRuleClass, "ifChildren", "Ljava/util/List;");
|
||||
|
||||
RenderingRuleStoragePropertiesClass = findClass(env, "net/osmand/render/RenderingRuleStorageProperties");
|
||||
RenderingRuleStorageProperties_rules = env->GetFieldID(RenderingRuleStoragePropertiesClass, "rules",
|
||||
"Ljava/util/List;");
|
||||
|
||||
RenderingRulePropertyClass = findClass(env, "net/osmand/render/RenderingRuleProperty");
|
||||
RenderingRuleProperty_type = env->GetFieldID(RenderingRulePropertyClass, "type", "I");
|
||||
RenderingRuleProperty_input = env->GetFieldID(RenderingRulePropertyClass, "input", "Z");
|
||||
RenderingRuleProperty_attrName = env->GetFieldID(RenderingRulePropertyClass, "attrName",
|
||||
"Ljava/lang/String;");
|
||||
|
||||
RenderingRulesStorageClass = findClass(env, "net/osmand/render/RenderingRulesStorage");
|
||||
RenderingRulesStorageClass_dictionary = env->GetFieldID(RenderingRulesStorageClass, "dictionary",
|
||||
"Ljava/util/List;");
|
||||
RenderingRulesStorage_PROPS = env->GetFieldID(RenderingRulesStorageClass, "PROPS",
|
||||
"Lnet/osmand/render/RenderingRuleStorageProperties;");
|
||||
RenderingRulesStorage_getRules = env->GetMethodID(RenderingRulesStorageClass, "getRules",
|
||||
"(I)[Lnet/osmand/render/RenderingRule;");
|
||||
|
||||
ListClass = findClass(env, "java/util/List");
|
||||
List_size = env->GetMethodID(ListClass, "size", "()I");
|
||||
List_get = env->GetMethodID(ListClass, "get", "(I)Ljava/lang/Object;");
|
||||
|
||||
RenderingRuleSearchRequestClass = findClass(env, "net/osmand/render/RenderingRuleSearchRequest");
|
||||
RenderingRuleSearchRequest_storage = env->GetFieldID(RenderingRuleSearchRequestClass, "storage",
|
||||
"Lnet/osmand/render/RenderingRulesStorage;");
|
||||
RenderingRuleSearchRequest_props = env->GetFieldID(RenderingRuleSearchRequestClass, "props",
|
||||
"[Lnet/osmand/render/RenderingRuleProperty;");
|
||||
RenderingRuleSearchRequest_values = env->GetFieldID(RenderingRuleSearchRequestClass, "values", "[I");
|
||||
RenderingRuleSearchRequest_fvalues = env->GetFieldID(RenderingRuleSearchRequestClass, "fvalues", "[F");
|
||||
RenderingRuleSearchRequest_savedValues = env->GetFieldID(RenderingRuleSearchRequestClass, "savedValues",
|
||||
"[I");
|
||||
RenderingRuleSearchRequest_savedFvalues = env->GetFieldID(RenderingRuleSearchRequestClass, "savedFvalues",
|
||||
"[F");
|
||||
|
||||
}
|
||||
|
||||
void unloadJniRenderRules(JNIEnv* env) {
|
||||
env->DeleteGlobalRef(RenderingRuleSearchRequestClass);
|
||||
env->DeleteGlobalRef(RenderingRuleClass);
|
||||
env->DeleteGlobalRef(RenderingRulePropertyClass);
|
||||
env->DeleteGlobalRef(RenderingRuleStoragePropertiesClass);
|
||||
env->DeleteGlobalRef(RenderingRulesStorageClass);
|
||||
env->DeleteGlobalRef(ListClass);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,20 +1,19 @@
|
|||
#ifndef _JAVA_WRAP_CPP
|
||||
#define _JAVA_WRAP_CPP
|
||||
|
||||
#include "common.h"
|
||||
#include "java_wrap.h"
|
||||
#include "binaryRead.h"
|
||||
#include "rendering.h"
|
||||
#include <dlfcn.h>
|
||||
#include <SkBitmap.h>
|
||||
#include <SkCanvas.h>
|
||||
#include <SkImageDecoder.h>
|
||||
#include "java_renderRules.h"
|
||||
#include "common.h"
|
||||
#include "java_wrap.h"
|
||||
#include "binaryRead.h"
|
||||
#include "rendering.h"
|
||||
|
||||
JavaVM* globalJVM = NULL;
|
||||
// Forward declarations
|
||||
void loadJniCommon(JNIEnv* env);
|
||||
void loadJniRenderingContext(JNIEnv* env);
|
||||
void loadJniRenderingRules(JNIEnv* env);
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
|
||||
{
|
||||
JNIEnv* globalJniEnv;
|
||||
|
@ -22,7 +21,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
|
|||
return JNI_ERR; /* JNI version not supported */
|
||||
globalJVM = vm;
|
||||
|
||||
loadJniCommon(globalJniEnv);
|
||||
loadJniRenderingContext(globalJniEnv);
|
||||
loadJniRenderingRules(globalJniEnv);
|
||||
osmand_log_print(LOG_INFO, "JNI_OnLoad completed");
|
||||
|
||||
|
@ -58,6 +57,33 @@ extern "C" JNIEXPORT jboolean JNICALL Java_net_osmand_plus_render_NativeOsmandLi
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Global object
|
||||
HMAP::hash_map<void*, RenderingRulesStorage*> cachedStorages;
|
||||
|
||||
RenderingRulesStorage* getStorage(JNIEnv* env, jobject storage) {
|
||||
if (cachedStorages.find(storage) == cachedStorages.end()) {
|
||||
cachedStorages[storage] = createRenderingRulesStorage(env, storage);
|
||||
}
|
||||
return cachedStorages[storage];
|
||||
}
|
||||
extern "C" JNIEXPORT void JNICALL Java_net_osmand_plus_render_NativeOsmandLibrary_initRenderingRulesStorage(JNIEnv* ienv,
|
||||
jobject obj, jobject storage) {
|
||||
getStorage(ienv, storage);
|
||||
}
|
||||
|
||||
|
||||
RenderingRuleSearchRequest* initSearchRequest(JNIEnv* env, jobject renderingRuleSearchRequest) {
|
||||
jobject storage = env->GetObjectField(renderingRuleSearchRequest, RenderingRuleSearchRequest_storage);
|
||||
RenderingRulesStorage* st = getStorage(env, storage);
|
||||
env->DeleteLocalRef(storage);
|
||||
RenderingRuleSearchRequest* res = new RenderingRuleSearchRequest(st);
|
||||
initRenderingRuleSearchRequest(env, res, renderingRuleSearchRequest);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL Java_net_osmand_plus_render_NativeOsmandLibrary_searchNativeObjectsForRendering(JNIEnv* ienv,
|
||||
jobject obj, jint sleft, jint sright, jint stop, jint sbottom, jint zoom,
|
||||
jobject renderingRuleSearchRequest, bool skipDuplicates, jobject objInterrupted, jstring msgNothingFound) {
|
||||
|
@ -75,9 +101,9 @@ extern "C" JNIEXPORT jint JNICALL Java_net_osmand_plus_render_NativeOsmandLibrar
|
|||
return (jint) res;
|
||||
}
|
||||
|
||||
RenderingRuleSearchRequest* initSearchRequest(JNIEnv* env, jobject renderingRuleSearchRequest) {
|
||||
return new RenderingRuleSearchRequest(env, renderingRuleSearchRequest);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
///////////// JNI RENDERING //////////////
|
||||
|
||||
#ifdef ANDROID_BUILD
|
||||
#include <android/bitmap.h>
|
||||
|
@ -277,6 +303,9 @@ extern "C" JNIEXPORT jobject JNICALL Java_net_osmand_plus_render_NativeOsmandLib
|
|||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////
|
||||
////////// JNI Rendering Context //////////////
|
||||
|
||||
jclass jclass_JUnidecode;
|
||||
jmethodID jmethod_JUnidecode_unidecode;
|
||||
jclass jclass_RenderingContext = NULL;
|
||||
|
@ -306,7 +335,7 @@ jfieldID jfield_RenderingContext_ctx = NULL;
|
|||
jfieldID jfield_RenderingContext_textRenderingTime = NULL;
|
||||
jfieldID jfield_RenderingContext_lastRenderedKey = NULL;
|
||||
|
||||
void loadJniCommon(JNIEnv* env)
|
||||
void loadJniRenderingContext(JNIEnv* env)
|
||||
{
|
||||
jclass_RenderingContext = findClass(env, "net/osmand/plus/render/OsmandRenderer$RenderingContext");
|
||||
jfield_RenderingContext_interrupted = getFid(env, jclass_RenderingContext, "interrupted", "Z");
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#ifndef _OSMAND_RENDER_RULES
|
||||
#define _OSMAND_RENDER_RULES
|
||||
|
||||
#include <jni.h>
|
||||
#include "osmand_log.h"
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
|
@ -11,48 +10,6 @@
|
|||
|
||||
|
||||
|
||||
jclass ListClass;
|
||||
jmethodID List_size;
|
||||
jmethodID List_get;
|
||||
|
||||
jclass RenderingRuleClass;
|
||||
jfieldID RenderingRule_properties;
|
||||
jfieldID RenderingRule_intProperties;
|
||||
jfieldID RenderingRule_floatProperties;
|
||||
jfieldID RenderingRule_ifElseChildren;
|
||||
jfieldID RenderingRule_ifChildren;
|
||||
|
||||
jclass RenderingRuleStoragePropertiesClass;
|
||||
jfieldID RenderingRuleStorageProperties_rules;
|
||||
|
||||
jclass RenderingRulePropertyClass;
|
||||
jfieldID RenderingRuleProperty_type;
|
||||
jfieldID RenderingRuleProperty_input;
|
||||
jfieldID RenderingRuleProperty_attrName;
|
||||
|
||||
jclass RenderingRulesStorageClass;
|
||||
jfieldID RenderingRulesStorageClass_dictionary;
|
||||
jfieldID RenderingRulesStorage_PROPS;
|
||||
jmethodID RenderingRulesStorage_getRules;
|
||||
|
||||
jclass RenderingRuleSearchRequestClass;
|
||||
jfieldID RenderingRuleSearchRequest_storage;
|
||||
jfieldID RenderingRuleSearchRequest_props;
|
||||
jfieldID RenderingRuleSearchRequest_values;
|
||||
jfieldID RenderingRuleSearchRequest_fvalues;
|
||||
jfieldID RenderingRuleSearchRequest_savedValues;
|
||||
jfieldID RenderingRuleSearchRequest_savedFvalues;
|
||||
|
||||
/// TODO Forward declaration
|
||||
jobject newGlobalRef(JNIEnv* env, jobject o);
|
||||
void throwNewException(JNIEnv* env, const char* msg);
|
||||
jfieldID getFid(JNIEnv* env, jclass cls, const char* fieldName, const char* sig);
|
||||
std::string getStringField(JNIEnv* env, jobject o, jfieldID fid);
|
||||
jclass findClass(JNIEnv* env, const char* className, bool mustHave = true);
|
||||
|
||||
|
||||
|
||||
|
||||
int RenderingRulesStorage::getPropertiesSize() {
|
||||
return properties.size();
|
||||
}
|
||||
|
@ -89,230 +46,10 @@ int RenderingRulesStorage::getDictionaryValue(std::string s) {
|
|||
return dictionaryMap[s];
|
||||
}
|
||||
|
||||
void RenderingRulesStorage::initDictionary(JNIEnv* env) {
|
||||
jobject listDictionary = env->GetObjectField(javaStorage, RenderingRulesStorageClass_dictionary);
|
||||
uint sz = env->CallIntMethod(listDictionary, List_size);
|
||||
uint i = 0;
|
||||
for (; i < sz; i++) {
|
||||
jstring st = (jstring) env->CallObjectMethod(listDictionary, List_get, i);
|
||||
// if(st != NULL)
|
||||
// {
|
||||
const char* utf = env->GetStringUTFChars(st, NULL);
|
||||
std::string d = std::string(utf);
|
||||
|
||||
env->ReleaseStringUTFChars(st, utf);
|
||||
env->DeleteLocalRef(st);
|
||||
dictionary.push_back(d);
|
||||
dictionaryMap[d] = i;
|
||||
// }
|
||||
}
|
||||
env->DeleteLocalRef(listDictionary);
|
||||
}
|
||||
|
||||
void RenderingRulesStorage::initProperties(JNIEnv* env) {
|
||||
jobject props = env->GetObjectField(javaStorage, RenderingRulesStorage_PROPS);
|
||||
jobject listProps = env->GetObjectField(props, RenderingRuleStorageProperties_rules);
|
||||
uint sz = env->CallIntMethod(listProps, List_size);
|
||||
uint i = 0;
|
||||
for (; i < sz; i++) {
|
||||
jobject rulePrope = env->CallObjectMethod(listProps, List_get, i);
|
||||
bool input = (env->GetBooleanField(rulePrope, RenderingRuleProperty_input) == JNI_TRUE);
|
||||
int type = env->GetIntField(rulePrope, RenderingRuleProperty_type);
|
||||
std::string name = getStringField(env, rulePrope, RenderingRuleProperty_attrName);
|
||||
RenderingRuleProperty* prop = new RenderingRuleProperty(type, input, name, i);
|
||||
properties.push_back(*prop);
|
||||
propertyMap[name] = prop;
|
||||
env->DeleteLocalRef(rulePrope);
|
||||
}
|
||||
env->DeleteLocalRef(props);
|
||||
env->DeleteLocalRef(listProps);
|
||||
|
||||
}
|
||||
|
||||
void RenderingRulesStorage::initRules(JNIEnv* env) {
|
||||
for (int i = 1; i < SIZE_STATES; i++) {
|
||||
jobjectArray rules = (jobjectArray) env->CallObjectMethod(javaStorage, RenderingRulesStorage_getRules,
|
||||
i);
|
||||
jsize len = env->GetArrayLength(rules);
|
||||
for (jsize j = 0; j < len; j++) {
|
||||
jobject rRule = env->GetObjectArrayElement(rules, j);
|
||||
RenderingRule* rule = createRenderingRule(env, rRule);
|
||||
env->DeleteLocalRef(rRule);
|
||||
if (rule != NULL) {
|
||||
|
||||
jsize psz = rule->properties.size();
|
||||
int tag = -1;
|
||||
int value = -1;
|
||||
for (int p = 0; p < psz; p++) {
|
||||
if (rule->properties.at(p)->attrName == "tag") {
|
||||
tag = rule->intProperties.at(p);
|
||||
} else if (rule->properties.at(p)->attrName == "value") {
|
||||
value = rule->intProperties.at(p);
|
||||
}
|
||||
}
|
||||
if (tag != -1 && value != -1) {
|
||||
int key = (tag << SHIFT_TAG_VAL) + value;
|
||||
tagValueGlobalRules[i][key] = *rule;
|
||||
}
|
||||
}
|
||||
}
|
||||
env->DeleteLocalRef(rules);
|
||||
}
|
||||
}
|
||||
|
||||
RenderingRule* RenderingRulesStorage::createRenderingRule(JNIEnv* env, jobject rRule) {
|
||||
RenderingRule* rule = new RenderingRule;
|
||||
jobjectArray props = (jobjectArray) env->GetObjectField(rRule, RenderingRule_properties);
|
||||
jintArray intProps = (jintArray) env->GetObjectField(rRule, RenderingRule_intProperties);
|
||||
jfloatArray floatProps = (jfloatArray) env->GetObjectField(rRule, RenderingRule_floatProperties);
|
||||
jobject ifChildren = env->GetObjectField(rRule, RenderingRule_ifChildren);
|
||||
jobject ifElseChildren = env->GetObjectField(rRule, RenderingRule_ifElseChildren);
|
||||
|
||||
jsize sz = env->GetArrayLength(props);
|
||||
|
||||
if (floatProps != NULL) {
|
||||
jfloat* fe = env->GetFloatArrayElements(floatProps, NULL);
|
||||
for (int j = 0; j < sz; j++) {
|
||||
rule->floatProperties.push_back(fe[j]);
|
||||
}
|
||||
env->ReleaseFloatArrayElements(floatProps, fe, JNI_ABORT);
|
||||
env->DeleteLocalRef(floatProps);
|
||||
} else {
|
||||
rule->floatProperties.assign(sz, 0);
|
||||
}
|
||||
|
||||
if (intProps != NULL) {
|
||||
jint* ie = env->GetIntArrayElements(intProps, NULL);
|
||||
for (int j = 0; j < sz; j++) {
|
||||
rule->intProperties.push_back(ie[j]);
|
||||
}
|
||||
env->ReleaseIntArrayElements(intProps, ie, JNI_ABORT);
|
||||
env->DeleteLocalRef(intProps);
|
||||
} else {
|
||||
rule->intProperties.assign(sz, -1);
|
||||
}
|
||||
|
||||
for (jsize i = 0; i < sz; i++) {
|
||||
jobject prop = env->GetObjectArrayElement(props, i);
|
||||
std::string attr = getStringField(env, prop, RenderingRuleProperty_attrName);
|
||||
RenderingRuleProperty* p = getProperty(attr.c_str());
|
||||
rule->properties.push_back(p);
|
||||
env->DeleteLocalRef(prop);
|
||||
}
|
||||
env->DeleteLocalRef(props);
|
||||
|
||||
if (ifChildren != NULL) {
|
||||
sz = env->CallIntMethod(ifChildren, List_size);
|
||||
for (jsize i = 0; i < sz; i++) {
|
||||
jobject o = env->CallObjectMethod(ifChildren, List_get, i);
|
||||
rule->ifChildren.push_back(*createRenderingRule(env, o));
|
||||
env->DeleteLocalRef(o);
|
||||
}
|
||||
env->DeleteLocalRef(ifChildren);
|
||||
}
|
||||
|
||||
if (ifElseChildren != NULL) {
|
||||
sz = env->CallIntMethod(ifElseChildren, List_size);
|
||||
for (jsize i = 0; i < sz; i++) {
|
||||
jobject o = env->CallObjectMethod(ifElseChildren, List_get, i);
|
||||
rule->ifElseChildren.push_back(*createRenderingRule(env, o));
|
||||
env->DeleteLocalRef(o);
|
||||
}
|
||||
env->DeleteLocalRef(ifElseChildren);
|
||||
}
|
||||
|
||||
return rule;
|
||||
}
|
||||
|
||||
// Global object
|
||||
RenderingRulesStorage* defaultStorage = NULL;
|
||||
|
||||
void RenderingRuleSearchRequest::initObject(JNIEnv* env, jobject rrs) {
|
||||
jsize sz;
|
||||
jobjectArray oa = (jobjectArray) env->GetObjectField(rrs, RenderingRuleSearchRequest_props);
|
||||
sz = env->GetArrayLength(oa);
|
||||
std::vector<RenderingRuleProperty*> requestProps;
|
||||
for (jsize i = 0; i < sz; i++) {
|
||||
jobject prop = env->GetObjectArrayElement(oa, i);
|
||||
std::string attr = getStringField(env, prop, RenderingRuleProperty_attrName);
|
||||
RenderingRuleProperty* p = storage->getProperty(attr.c_str());
|
||||
requestProps.push_back(p);
|
||||
env->DeleteLocalRef(prop);
|
||||
}
|
||||
env->DeleteLocalRef(oa);
|
||||
sz = storage->getPropertiesSize();
|
||||
{
|
||||
values = new int[sz];
|
||||
jintArray ia = (jintArray) env->GetObjectField(rrs, RenderingRuleSearchRequest_values);
|
||||
jint* ie = env->GetIntArrayElements(ia, NULL);
|
||||
for (int i = 0; i < sz; i++) {
|
||||
values[requestProps.at(i)->id] = ie[i];
|
||||
}
|
||||
env->ReleaseIntArrayElements(ia, ie, JNI_ABORT);
|
||||
env->DeleteLocalRef(ia);
|
||||
}
|
||||
|
||||
{
|
||||
fvalues = new float[sz];
|
||||
jfloatArray ia = (jfloatArray) env->GetObjectField(rrs, RenderingRuleSearchRequest_fvalues);
|
||||
jfloat* ie = env->GetFloatArrayElements(ia, NULL);
|
||||
for (int i = 0; i < sz; i++) {
|
||||
fvalues[requestProps.at(i)->id] = ie[i];
|
||||
}
|
||||
env->ReleaseFloatArrayElements(ia, ie, JNI_ABORT);
|
||||
env->DeleteLocalRef(ia);
|
||||
}
|
||||
|
||||
{
|
||||
savedValues = new int[sz];
|
||||
jintArray ia = (jintArray) env->GetObjectField(rrs, RenderingRuleSearchRequest_values);
|
||||
jint* ie = env->GetIntArrayElements(ia, NULL);
|
||||
for (int i = 0; i < sz; i++) {
|
||||
savedValues[requestProps.at(i)->id] = ie[i];
|
||||
}
|
||||
env->ReleaseIntArrayElements(ia, ie, JNI_ABORT);
|
||||
env->DeleteLocalRef(ia);
|
||||
}
|
||||
|
||||
{
|
||||
savedFvalues = new float[sz];
|
||||
jfloatArray ia = (jfloatArray) env->GetObjectField(rrs, RenderingRuleSearchRequest_fvalues);
|
||||
jfloat* ie = env->GetFloatArrayElements(ia, NULL);
|
||||
for (int i = 0; i < sz; i++) {
|
||||
savedFvalues[requestProps.at(i)->id] = ie[i];
|
||||
}
|
||||
env->ReleaseFloatArrayElements(ia, ie, JNI_ABORT);
|
||||
env->DeleteLocalRef(ia);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_net_osmand_plus_render_NativeOsmandLibrary_initRenderingRulesStorage(JNIEnv* ienv,
|
||||
jobject obj, jobject storage) {
|
||||
if (defaultStorage == NULL || defaultStorage->javaStorage != storage) {
|
||||
// multi thread will not work?
|
||||
if (defaultStorage != NULL) {
|
||||
delete defaultStorage;
|
||||
}
|
||||
defaultStorage = new RenderingRulesStorage(ienv, storage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RenderingRuleSearchRequest::RenderingRuleSearchRequest(JNIEnv* env, jobject rrs) :
|
||||
renderingRuleSearch(rrs) {
|
||||
jobject storage = env->GetObjectField(rrs, RenderingRuleSearchRequest_storage);
|
||||
if (defaultStorage == NULL || defaultStorage->javaStorage != storage) {
|
||||
// multi thread will not work?
|
||||
if (defaultStorage != NULL) {
|
||||
delete defaultStorage;
|
||||
}
|
||||
defaultStorage = new RenderingRulesStorage(env, storage);
|
||||
}
|
||||
env->DeleteLocalRef(storage);
|
||||
this->storage = defaultStorage;
|
||||
RenderingRuleSearchRequest::RenderingRuleSearchRequest(RenderingRulesStorage* storage) {
|
||||
this->storage = storage;
|
||||
PROPS = new RenderingRulesStorageProperties(this->storage);
|
||||
initObject(env, rrs);
|
||||
clearState();
|
||||
}
|
||||
|
||||
|
@ -366,6 +103,13 @@ void RenderingRuleSearchRequest::setIntFilter(RenderingRuleProperty* p, int filt
|
|||
}
|
||||
}
|
||||
|
||||
void RenderingRuleSearchRequest::externalInitialize(int* vs, float* fvs, int* sVs, float* sFvs){
|
||||
this->values = vs;
|
||||
this->fvalues = fvs;
|
||||
this->savedFvalues = sFvs;
|
||||
this->savedValues = sVs;
|
||||
|
||||
}
|
||||
void RenderingRuleSearchRequest::clearIntvalue(RenderingRuleProperty* p) {
|
||||
if (p != NULL) {
|
||||
// assert !p->input;
|
||||
|
@ -506,59 +250,4 @@ void RenderingRuleSearchRequest::setTagValueZoomLayer(std::string tag, std::stri
|
|||
}
|
||||
|
||||
|
||||
void loadJniRenderingRules(JNIEnv* env) {
|
||||
RenderingRuleClass = findClass(env, "net/osmand/render/RenderingRule");
|
||||
RenderingRule_properties = env->GetFieldID(RenderingRuleClass, "properties",
|
||||
"[Lnet/osmand/render/RenderingRuleProperty;");
|
||||
RenderingRule_intProperties = env->GetFieldID(RenderingRuleClass, "intProperties", "[I");
|
||||
RenderingRule_floatProperties = env->GetFieldID(RenderingRuleClass, "floatProperties", "[F");
|
||||
RenderingRule_ifElseChildren = env->GetFieldID(RenderingRuleClass, "ifElseChildren", "Ljava/util/List;");
|
||||
RenderingRule_ifChildren = env->GetFieldID(RenderingRuleClass, "ifChildren", "Ljava/util/List;");
|
||||
|
||||
RenderingRuleStoragePropertiesClass = findClass(env, "net/osmand/render/RenderingRuleStorageProperties");
|
||||
RenderingRuleStorageProperties_rules = env->GetFieldID(RenderingRuleStoragePropertiesClass, "rules",
|
||||
"Ljava/util/List;");
|
||||
|
||||
RenderingRulePropertyClass = findClass(env, "net/osmand/render/RenderingRuleProperty");
|
||||
RenderingRuleProperty_type = env->GetFieldID(RenderingRulePropertyClass, "type", "I");
|
||||
RenderingRuleProperty_input = env->GetFieldID(RenderingRulePropertyClass, "input", "Z");
|
||||
RenderingRuleProperty_attrName = env->GetFieldID(RenderingRulePropertyClass, "attrName",
|
||||
"Ljava/lang/String;");
|
||||
|
||||
RenderingRulesStorageClass = findClass(env, "net/osmand/render/RenderingRulesStorage");
|
||||
RenderingRulesStorageClass_dictionary = env->GetFieldID(RenderingRulesStorageClass, "dictionary",
|
||||
"Ljava/util/List;");
|
||||
RenderingRulesStorage_PROPS = env->GetFieldID(RenderingRulesStorageClass, "PROPS",
|
||||
"Lnet/osmand/render/RenderingRuleStorageProperties;");
|
||||
RenderingRulesStorage_getRules = env->GetMethodID(RenderingRulesStorageClass, "getRules",
|
||||
"(I)[Lnet/osmand/render/RenderingRule;");
|
||||
|
||||
ListClass = findClass(env, "java/util/List");
|
||||
List_size = env->GetMethodID(ListClass, "size", "()I");
|
||||
List_get = env->GetMethodID(ListClass, "get", "(I)Ljava/lang/Object;");
|
||||
|
||||
RenderingRuleSearchRequestClass = findClass(env, "net/osmand/render/RenderingRuleSearchRequest");
|
||||
RenderingRuleSearchRequest_storage = env->GetFieldID(RenderingRuleSearchRequestClass, "storage",
|
||||
"Lnet/osmand/render/RenderingRulesStorage;");
|
||||
RenderingRuleSearchRequest_props = env->GetFieldID(RenderingRuleSearchRequestClass, "props",
|
||||
"[Lnet/osmand/render/RenderingRuleProperty;");
|
||||
RenderingRuleSearchRequest_values = env->GetFieldID(RenderingRuleSearchRequestClass, "values", "[I");
|
||||
RenderingRuleSearchRequest_fvalues = env->GetFieldID(RenderingRuleSearchRequestClass, "fvalues", "[F");
|
||||
RenderingRuleSearchRequest_savedValues = env->GetFieldID(RenderingRuleSearchRequestClass, "savedValues",
|
||||
"[I");
|
||||
RenderingRuleSearchRequest_savedFvalues = env->GetFieldID(RenderingRuleSearchRequestClass, "savedFvalues",
|
||||
"[F");
|
||||
|
||||
}
|
||||
|
||||
void unloadJniRenderRules(JNIEnv* env) {
|
||||
env->DeleteGlobalRef(RenderingRuleSearchRequestClass);
|
||||
env->DeleteGlobalRef(RenderingRuleClass);
|
||||
env->DeleteGlobalRef(RenderingRulePropertyClass);
|
||||
env->DeleteGlobalRef(RenderingRuleStoragePropertiesClass);
|
||||
env->DeleteGlobalRef(RenderingRulesStorageClass);
|
||||
env->DeleteGlobalRef(ListClass);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#ifndef _OSMAND_RENDER_RULES_H
|
||||
#define _OSMAND_RENDER_RULES_H
|
||||
|
||||
#include <jni.h>
|
||||
#include <string>
|
||||
#include "common.h"
|
||||
#include "mapObjects.h"
|
||||
|
@ -50,7 +49,8 @@ public:
|
|||
|
||||
class RenderingRulesStorage
|
||||
{
|
||||
private:
|
||||
// TODO make private
|
||||
public:
|
||||
const static int SHIFT_TAG_VAL = 16;
|
||||
const static int SIZE_STATES = 7;
|
||||
HMAP::hash_map<std::string, int> dictionaryMap;
|
||||
|
@ -60,11 +60,6 @@ private:
|
|||
HMAP::hash_map<std::string, RenderingRuleProperty*> propertyMap;
|
||||
|
||||
|
||||
RenderingRule* createRenderingRule(JNIEnv* env, jobject rRule);
|
||||
void initDictionary(JNIEnv* env);
|
||||
void initProperties(JNIEnv* env);
|
||||
void initRules(JNIEnv* env);
|
||||
|
||||
public:
|
||||
// No rules for multipolygon !!!
|
||||
const static int MULTI_POLYGON_TYPE = 0;
|
||||
|
@ -74,19 +69,16 @@ public:
|
|||
const static int POLYGON_RULES = 3;
|
||||
const static int TEXT_RULES = 4;
|
||||
const static int ORDER_RULES = 5;
|
||||
RenderingRulesStorage(JNIEnv* env, jobject storage) :
|
||||
javaStorage(storage) {
|
||||
RenderingRulesStorage(void* storage) :
|
||||
storageId(storage) {
|
||||
tagValueGlobalRules = new HMAP::hash_map<int, RenderingRule >[SIZE_STATES];
|
||||
initDictionary(env);
|
||||
initProperties(env);
|
||||
initRules(env);
|
||||
}
|
||||
|
||||
~RenderingRulesStorage() {
|
||||
delete[] tagValueGlobalRules;
|
||||
// proper
|
||||
}
|
||||
jobject javaStorage;
|
||||
void* storageId;
|
||||
|
||||
int getPropertiesSize();
|
||||
|
||||
|
@ -102,6 +94,7 @@ public:
|
|||
|
||||
};
|
||||
|
||||
|
||||
class RenderingRulesStorageProperties
|
||||
{
|
||||
public:
|
||||
|
@ -201,8 +194,6 @@ public:
|
|||
class RenderingRuleSearchRequest
|
||||
{
|
||||
private :
|
||||
jobject renderingRuleSearch;
|
||||
RenderingRulesStorage* storage;
|
||||
RenderingRulesStorageProperties* PROPS;
|
||||
int* values;
|
||||
float* fvalues;
|
||||
|
@ -212,10 +203,11 @@ private :
|
|||
MapDataObject* obj;
|
||||
|
||||
bool searchInternal(int state, int tagKey, int valueKey, bool loadOutput);
|
||||
void initObject(JNIEnv* env, jobject rrs);
|
||||
bool visitRule(RenderingRule* rule, bool loadOutput);
|
||||
public:
|
||||
RenderingRuleSearchRequest(JNIEnv* env, jobject rrs);
|
||||
RenderingRulesStorage* storage;
|
||||
|
||||
RenderingRuleSearchRequest(RenderingRulesStorage* storage);
|
||||
|
||||
~RenderingRuleSearchRequest();
|
||||
|
||||
|
@ -247,13 +239,11 @@ public:
|
|||
|
||||
void setTagValueZoomLayer(std::string tag, std::string val, int zoom, int layer, MapDataObject* obj);
|
||||
|
||||
void externalInitialize(int* values, float* fvalues, int* savedValues, float* savedFvalues);
|
||||
|
||||
};
|
||||
|
||||
|
||||
RenderingRuleSearchRequest* initSearchRequest(JNIEnv* env, jobject renderingRuleSearchRequest);
|
||||
|
||||
void loadJNIRenderingRules(JNIEnv* env);
|
||||
|
||||
void unloadJniRenderRules(JNIEnv* env);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -274,7 +274,7 @@ void drawOneWayPaints(RenderingContext* rc, SkCanvas* cv, SkPath* p) {
|
|||
|
||||
void drawPolyline(MapDataObject* mObj, RenderingRuleSearchRequest* req, SkCanvas* cv, SkPaint* paint,
|
||||
RenderingContext* rc, tag_value pair, int layer, int drawOnlyShadow) {
|
||||
jint length = mObj->points.size();
|
||||
size_t length = mObj->points.size();
|
||||
if (length < 2) {
|
||||
return;
|
||||
}
|
||||
|
@ -338,7 +338,7 @@ void drawPolyline(MapDataObject* mObj, RenderingRuleSearchRequest* req, SkCanvas
|
|||
|
||||
void drawPolygon(MapDataObject* mObj, RenderingRuleSearchRequest* req, SkCanvas* cv, SkPaint* paint,
|
||||
RenderingContext* rc, tag_value pair) {
|
||||
jint length = mObj->points.size();
|
||||
size_t length = mObj->points.size();
|
||||
if (length <= 2) {
|
||||
return;
|
||||
}
|
||||
|
@ -405,7 +405,7 @@ void drawPoint(MapDataObject* mObj, RenderingRuleSearchRequest* req, SkCanvas* c
|
|||
if (!bmp && !renderText)
|
||||
return;
|
||||
|
||||
jint length = mObj->points.size();
|
||||
size_t length = mObj->points.size();
|
||||
rc->visible++;
|
||||
float px = 0;
|
||||
float py = 0;
|
||||
|
|
Loading…
Reference in a new issue