package {{ package_name }};
final class {{ closure.callbacks_class_name }} {
private static final java.util.concurrent.ConcurrentHashMap<Long, {{ closure.interface_name }}> HANDLE_MAP = new java.util.concurrent.ConcurrentHashMap<>();
private static final java.util.concurrent.atomic.AtomicLong COUNTER = new java.util.concurrent.atomic.AtomicLong(1L);
{%- if closure.supports_proxy_wrap && closure.supports_cleaner %}
private static final java.lang.ref.Cleaner CLEANER = java.lang.ref.Cleaner.create();
{%- endif %}
private {{ closure.callbacks_class_name }}() {}
private static long insert({{ closure.interface_name }} impl_) {
long handle = COUNTER.getAndAdd(2L);
HANDLE_MAP.put(handle, impl_);
return handle;
}
public static void free(long handle) {
HANDLE_MAP.remove(handle);
}
public static long clone(long handle) {
{{ closure.interface_name }} obj = HANDLE_MAP.get(handle);
return obj != null ? insert(obj) : 0L;
}
{%- if closure.supports_proxy_wrap && closure.supports_cleaner %}
private static final class ProxyReleaser implements Runnable {
private long handle;
private ProxyReleaser(long handle) {
this.handle = handle;
}
@Override
public void run() {
if (handle == 0L) return;
Native.{{ closure.proxy_release_native_name() }}(handle);
handle = 0L;
}
}
{%- endif %}
{%- if closure.supports_proxy_wrap %}
private static final class Proxy implements {{ closure.interface_name }} {
private final long handle;
{%- if closure.supports_cleaner %}
private final java.lang.ref.Cleaner.Cleanable cleanable;
{%- endif %}
private Proxy(long handle) {
this.handle = handle;
{%- if closure.supports_cleaner %}
this.cleanable = CLEANER.register(this, new ProxyReleaser(handle));
{%- endif %}
}
long rawHandle() {
return handle;
}
{%- if !closure.supports_cleaner %}
@Override
protected void finalize() throws Throwable {
try {
if (handle != 0L) {
Native.{{ closure.proxy_release_native_name() }}(handle);
}
} finally {
super.finalize();
}
}
{%- endif %}
@Override
public {{ closure.proxy.return_type }} invoke({% for param in closure.proxy.params %}{{ param.java_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %}) {
{%- let proxy = closure.proxy %}
{%- let handle_name = "handle" %}
{%- include "render_java/proxy_sync_method_body.txt" %}
}
}
{%- endif %}
{%- if closure.supports_proxy_wrap %}
static {{ closure.interface_name }} wrap(long handle) {
{{ closure.interface_name }} existing = HANDLE_MAP.get(handle);
return existing != null ? existing : new Proxy(handle);
}
{%- endif %}
public static {% match closure.return_info %}{% when Some with (ret) %}{{ ret.jni_type() }}{% when None %}void{% endmatch %} call(long handle{% for param in closure.params %}, {{ param.jni_type }} {{ param.name }}{% endfor %}) {
{{ closure.interface_name }} impl_ = HANDLE_MAP.get(handle);
if (impl_ == null) throw new RuntimeException("invalid closure handle");
{%- match closure.return_info %}
{%- when Some with (ret) %}
{%- match ret %}
{%- when crate::render::java::plan::JavaBridgeReturn::Value with (value_return) %}
{%- if value_return.is_encoded() %}
{{ value_return.java_type }} value = impl_.invoke({% for param in closure.params %}{{ param.decode_expr }}{% if !loop.last %}, {% endif %}{% endfor %});
try (WireWriter wire = new WireWriter({{ value_return.encode_size_expr() }})) {
{{ value_return.encode_expr() }};
return wire.toByteArray();
}
{%- else %}
return {{ value_return.direct_prefix() }}impl_.invoke({% for param in closure.params %}{{ param.decode_expr }}{% if !loop.last %}, {% endif %}{% endfor %}){{ value_return.direct_suffix() }};
{%- endif %}
{%- when crate::render::java::plan::JavaBridgeReturn::Result with (result_return) %}
BoltFFIResult<{{ result_return.ok_java_type }}, {{ result_return.err_java_type }}> result;
try {
result = BoltFFIResult.ok(impl_.invoke({% for param in closure.params %}{{ param.decode_expr }}{% if !loop.last %}, {% endif %}{% endfor %}));
{% if result_return.has_exception_class() %}
} catch ({{ result_return.exception_class() }} exception) {
result = BoltFFIResult.err(exception.getError());
{% endif %}
{% if result_return.error_capture.is_string %}
} catch (RuntimeException exception) {
result = BoltFFIResult.err(exception.getMessage());
{% endif %}
{% if !result_return.has_exception_class() && !result_return.error_capture.is_string %}
} catch (Exception exception) {
throw new RuntimeException("callback returned unexpected error", exception);
{% endif %}
}
try (WireWriter wire = new WireWriter({{ result_return.encode_size_expr }})) {
{{ result_return.encode_expr }};
return wire.toByteArray();
}
{%- endmatch %}
{%- when None %}
impl_.invoke({% for param in closure.params %}{{ param.decode_expr }}{% if !loop.last %}, {% endif %}{% endfor %});
{%- endmatch %}
}
static long create({{ closure.interface_name }} impl_) {
{%- if closure.supports_proxy_wrap %}
if (impl_ instanceof Proxy) {
return Native.{{ closure.proxy_clone_native_name() }}(((Proxy) impl_).rawHandle());
}
{%- endif %}
return insert(impl_);
}
}