Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.caoccao.javet.interop.converters.JavetObjectConverter Maven / Gradle / Ivy
Go to download
Javet is Java + V8 (JAVa + V + EighT). It is an awesome way of embedding Node.js and V8 in Java.
/*
* Copyright (c) 2021-2023. caoccao.com Sam Cao
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.caoccao.javet.interop.converters;
import com.caoccao.javet.annotations.CheckReturnValue;
import com.caoccao.javet.entities.JavetEntityFunction;
import com.caoccao.javet.entities.JavetEntityMap;
import com.caoccao.javet.entities.JavetEntitySymbol;
import com.caoccao.javet.enums.V8ValueReferenceType;
import com.caoccao.javet.exceptions.JavetException;
import com.caoccao.javet.interfaces.IJavetEntityFunction;
import com.caoccao.javet.interfaces.IJavetEntityMap;
import com.caoccao.javet.interop.V8Runtime;
import com.caoccao.javet.interop.V8Scope;
import com.caoccao.javet.interop.callback.JavetCallbackContext;
import com.caoccao.javet.interop.proxy.IJavetProxyHandler;
import com.caoccao.javet.interop.proxy.JavetDynamicProxyObjectHandler;
import com.caoccao.javet.values.V8Value;
import com.caoccao.javet.values.reference.*;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.BaseStream;
/**
* The type Javet object converter converts Java primitive types,
* Array, List, Map and Set to JS primitive types, Array, Map, Set
* and Object bi-directionally.
*
* @since 0.7.2
*/
@SuppressWarnings("unchecked")
public class JavetObjectConverter extends JavetPrimitiveConverter {
/**
* The constant EXECUTABLE_INDEX_DEFAULT_CONSTRUCTOR.
*
* @since 0.9.12
*/
protected static final int EXECUTABLE_INDEX_DEFAULT_CONSTRUCTOR = 0;
/**
* The constant EXECUTABLE_INDEX_FROM_MAP.
*
* @since 0.9.12
*/
protected static final int EXECUTABLE_INDEX_FROM_MAP = 1;
/**
* The constant EXECUTABLE_INDEX_TO_MAP.
*
* @since 0.9.12
*/
protected static final int EXECUTABLE_INDEX_TO_MAP = 2;
/**
* The constant METHOD_NAME_FROM_MAP.
*
* @since 0.9.12
*/
protected static final String METHOD_NAME_FROM_MAP = "fromMap";
/**
* The constant METHOD_NAME_TO_MAP.
*
* @since 0.9.12
*/
protected static final String METHOD_NAME_TO_MAP = "toMap";
/**
* The constant PRIVATE_PROPERTY_CUSTOM_OBJECT_CLASS_NAME.
*
* @since 0.9.6
*/
protected static final String PRIVATE_PROPERTY_CUSTOM_OBJECT_CLASS_NAME = "JavetObjectConverter#customObjectClassName";
/**
* The constant PRIVATE_PROPERTY_PROXY_TARGET.
*
* @since 0.9.6
*/
protected static final String PRIVATE_PROPERTY_PROXY_TARGET = "Javet#proxyTarget";
/**
* The constant PROPERTY_NAME.
*
* @since 0.7.2
*/
protected static final String PROPERTY_NAME = "name";
/**
* The constant PUBLIC_PROPERTY_CONSTRUCTOR.
*
* @since 0.7.2
*/
protected static final String PUBLIC_PROPERTY_CONSTRUCTOR = "constructor";
/**
* The Custom object map.
*
* @since 0.9.12
*/
protected Map customObjectMap;
/**
* Instantiates a new Javet object converter.
*
* @since 0.7.1
*/
public JavetObjectConverter() {
super();
customObjectMap = new ConcurrentHashMap<>();
}
/**
* Create entity function javet entity function.
*
* @return the javet entity function
* @since 0.9.4
*/
protected IJavetEntityFunction createEntityFunction() {
return new JavetEntityFunction();
}
/**
* Create entity map.
*
* @return the map
* @since 0.7.2
*/
protected Map createEntityMap() {
return new JavetEntityMap();
}
/**
* Register custom object.
*
* @param customObjectClass the custom object class
* @return true : success, false: failure
* @since 0.9.12
*/
@SuppressWarnings("UnusedReturnValue")
public boolean registerCustomObject(Class> customObjectClass) {
return registerCustomObject(customObjectClass, METHOD_NAME_FROM_MAP, METHOD_NAME_TO_MAP);
}
/**
* Register custom object.
*
* @param customObjectClass the custom object class
* @param methodNameFromMap the method name from map
* @param methodNameToMap the method name to map
* @return true : success, false: failure
* @since 0.9.12
*/
public boolean registerCustomObject(Class> customObjectClass, String methodNameFromMap, String methodNameToMap) {
if (customObjectClass == null
|| methodNameFromMap == null || methodNameToMap == null
|| methodNameFromMap.length() == 0 || methodNameToMap.length() == 0
|| methodNameFromMap.equals(methodNameToMap)) {
return false;
}
String customObjectClassName = customObjectClass.getName();
if (customObjectMap.containsKey(customObjectClassName)) {
return false;
}
try {
Constructor> defaultConstructor = customObjectClass.getConstructor();
Method methodFromMap = customObjectClass.getMethod(methodNameFromMap, Map.class);
if (Modifier.isStatic(methodFromMap.getModifiers())) {
return false;
}
Method methodToMap = customObjectClass.getMethod(methodNameToMap);
if (Modifier.isStatic(methodToMap.getModifiers())) {
return false;
}
AccessibleObject[] executables = new AccessibleObject[]{defaultConstructor, methodFromMap, methodToMap};
customObjectMap.put(customObjectClass.getName(), executables);
} catch (Throwable t) {
// Do nothing.
t.printStackTrace(System.err);
}
return false;
}
@Override
protected T toObject(V8Value v8Value, final int depth) throws JavetException {
T returnObject = super.toObject(v8Value, depth);
if (!(returnObject instanceof V8Value)) {
return returnObject;
}
if (v8Value instanceof V8ValueArray) {
V8ValueArray v8ValueArray = (V8ValueArray) v8Value;
final List list = new ArrayList<>();
v8ValueArray.forEach(value -> list.add(toObject(value, depth + 1)));
return (T) list;
} else if (v8Value instanceof V8ValueSet) {
V8ValueSet v8ValueSet = (V8ValueSet) v8Value;
final HashSet set = new HashSet<>();
v8ValueSet.forEach(key -> set.add(toObject(key, depth + 1)));
return (T) set;
} else if (v8Value instanceof V8ValueMap) {
V8ValueMap v8ValueMap = (V8ValueMap) v8Value;
final Map map = createEntityMap();
v8ValueMap.forEach((V8Value key, V8Value value) -> map.put(key.toString(), toObject(value, depth + 1)));
return (T) map;
} else if (v8Value instanceof V8ValueTypedArray) {
V8ValueTypedArray v8ValueTypedArray = (V8ValueTypedArray) v8Value;
switch (v8ValueTypedArray.getType()) {
case Int8Array:
case Uint8Array:
case Uint8ClampedArray:
return (T) v8ValueTypedArray.toBytes();
case Int16Array:
case Uint16Array:
return (T) v8ValueTypedArray.toShorts();
case Int32Array:
case Uint32Array:
return (T) v8ValueTypedArray.toIntegers();
case Float32Array:
return (T) v8ValueTypedArray.toFloats();
case Float64Array:
return (T) v8ValueTypedArray.toDoubles();
case BigInt64Array:
case BigUint64Array:
return (T) v8ValueTypedArray.toLongs();
default:
break;
}
} else if (v8Value instanceof V8ValueFunction) {
final IJavetEntityFunction javetEntityFunction = createEntityFunction();
if (config.isExtractFunctionSourceCode()) {
V8ValueFunction v8ValueFunction = (V8ValueFunction) v8Value;
javetEntityFunction.setJSFunctionType(v8ValueFunction.getJSFunctionType());
switch (javetEntityFunction.getJSFunctionType()) {
case Native:
case API:
javetEntityFunction.setSourceCode(v8ValueFunction.toString());
break;
case UserDefined:
javetEntityFunction.setSourceCode(v8ValueFunction.getSourceCode());
break;
default:
break;
}
}
return (T) javetEntityFunction;
} else if (v8Value instanceof V8ValueSymbol) {
final V8ValueSymbol v8ValueSymbol = (V8ValueSymbol) v8Value;
return (T) new JavetEntitySymbol(v8ValueSymbol.getDescription());
} else if (v8Value instanceof V8ValueObject) {
if (v8Value instanceof V8ValueProxy) {
final V8ValueProxy v8ValueProxy = (V8ValueProxy) v8Value;
try (IV8ValueObject iV8ValueObjectHandler = v8ValueProxy.getHandler()) {
Long handle = iV8ValueObjectHandler.getPrivatePropertyLong(PRIVATE_PROPERTY_PROXY_TARGET);
if (handle != null) {
JavetCallbackContext javetCallbackContext = v8ValueProxy.getV8Runtime().getCallbackContext(handle);
if (javetCallbackContext != null) {
IJavetProxyHandler iJavetProxyHandler =
(IJavetProxyHandler) javetCallbackContext.getCallbackReceiver();
Object targetObject = iJavetProxyHandler.getTargetObject();
if (targetObject != null) {
return (T) targetObject;
}
}
}
}
}
V8ValueObject v8ValueObject = (V8ValueObject) v8Value;
final Map map = new HashMap<>();
v8ValueObject.forEach((V8Value key, V8Value value) -> {
String keyString = key.toString();
if (PUBLIC_PROPERTY_CONSTRUCTOR.equals(keyString)) {
map.put(PUBLIC_PROPERTY_CONSTRUCTOR, ((V8ValueObject) value).getString(PROPERTY_NAME));
} else if (value.isUndefined()) {
// Pass
} else if (config.isSkipFunctionInObject() && value instanceof V8ValueFunction) {
// Pass
} else {
Object object = toObject(value, depth + 1);
map.put(keyString, object);
}
});
if (!customObjectMap.isEmpty()
&& v8ValueObject.hasPrivateProperty(PRIVATE_PROPERTY_CUSTOM_OBJECT_CLASS_NAME)) {
String customObjectClassName =
v8ValueObject.getPrivatePropertyString(PRIVATE_PROPERTY_CUSTOM_OBJECT_CLASS_NAME);
Constructor> defaultConstructor = null;
Method methodFromMap = null;
AccessibleObject[] executables = customObjectMap.get(customObjectClassName);
if (executables != null) {
defaultConstructor = (Constructor>) executables[EXECUTABLE_INDEX_DEFAULT_CONSTRUCTOR];
methodFromMap = (Method) executables[EXECUTABLE_INDEX_FROM_MAP];
}
if (defaultConstructor != null) {
try {
Object customObject = defaultConstructor.newInstance();
methodFromMap.invoke(customObject, map);
return (T) customObject;
} catch (Throwable t) {
// Do nothing
}
}
}
return (T) map;
}
return (T) v8Value;
}
@Override
@CheckReturnValue
protected T toV8Value(
V8Runtime v8Runtime, Object object, final int depth) throws JavetException {
V8Value v8Value = super.toV8Value(v8Runtime, object, depth);
if (v8Value != null && !(v8Value.isUndefined())) {
return (T) v8Value;
}
if (object instanceof IJavetEntityMap) {
try (V8Scope v8Scope = v8Runtime.getV8Scope()) {
V8ValueMap v8ValueMap = v8Scope.createV8ValueMap();
final Map, ?> mapObject = (Map, ?>) object;
for (Object key : mapObject.keySet()) {
try (V8Value childV8Value = toV8Value(v8Runtime, mapObject.get(key), depth + 1)) {
String childStringKey = key instanceof String ? (String) key : key.toString();
v8ValueMap.set(childStringKey, childV8Value);
}
}
v8Value = v8ValueMap;
v8Scope.setEscapable();
}
} else if (object instanceof Map) {
if (config.isProxyMapEnabled()) {
try (V8Scope v8Scope = v8Runtime.getV8Scope()) {
V8ValueProxy v8ValueProxy = v8Scope.createV8ValueProxy();
try (IV8ValueObject iV8ValueObjectHandler = v8ValueProxy.getHandler()) {
JavetDynamicProxyObjectHandler> javetProxyHandler =
new JavetDynamicProxyObjectHandler<>(
v8Runtime, null, (Map, ?>) object);
List javetCallbackContexts =
iV8ValueObjectHandler.bind(javetProxyHandler);
iV8ValueObjectHandler.setPrivateProperty(
PRIVATE_PROPERTY_PROXY_TARGET, javetCallbackContexts.get(0).getHandle());
}
v8Value = v8ValueProxy;
v8Scope.setEscapable();
}
} else {
try (V8Scope v8Scope = v8Runtime.getV8Scope()) {
V8ValueObject v8ValueObject = v8Scope.createV8ValueObject();
final Map, ?> mapObject = (Map, ?>) object;
for (Object key : mapObject.keySet()) {
try (V8Value childV8Value = toV8Value(v8Runtime, mapObject.get(key), depth + 1)) {
String childStringKey = key instanceof String ? (String) key : key.toString();
v8ValueObject.set(childStringKey, childV8Value);
}
}
v8Value = v8ValueObject;
v8Scope.setEscapable();
}
}
} else if (object instanceof Set) {
if (config.isProxySetEnabled()) {
try (V8Scope v8Scope = v8Runtime.getV8Scope()) {
V8ValueProxy v8ValueProxy = v8Scope.createV8ValueProxy();
try (IV8ValueObject iV8ValueObjectHandler = v8ValueProxy.getHandler()) {
JavetDynamicProxyObjectHandler> javetProxyHandler =
new JavetDynamicProxyObjectHandler<>(
v8Runtime, null, (Set>) object);
List javetCallbackContexts =
iV8ValueObjectHandler.bind(javetProxyHandler);
iV8ValueObjectHandler.setPrivateProperty(
PRIVATE_PROPERTY_PROXY_TARGET, javetCallbackContexts.get(0).getHandle());
}
v8Value = v8ValueProxy;
v8Scope.setEscapable();
}
} else {
try (V8Scope v8Scope = v8Runtime.getV8Scope()) {
V8ValueSet v8ValueSet = v8Scope.createV8ValueSet();
final Set> setObject = (Set>) object;
for (Object item : setObject) {
try (V8Value childV8Value = toV8Value(v8Runtime, item, depth + 1)) {
v8ValueSet.add(childV8Value);
}
}
v8Value = v8ValueSet;
v8Scope.setEscapable();
}
}
} else if (object instanceof Collection) {
try (V8Scope v8Scope = v8Runtime.getV8Scope()) {
V8ValueArray v8ValueArray = v8Scope.createV8ValueArray();
for (Object item : (Collection>) object) {
try (V8Value childV8Value = toV8Value(v8Runtime, item, depth + 1)) {
v8ValueArray.push(childV8Value);
}
}
v8Value = v8ValueArray;
v8Scope.setEscapable();
}
} else if (object instanceof BaseStream) {
try (V8Scope v8Scope = v8Runtime.getV8Scope()) {
V8ValueArray v8ValueArray = v8Scope.createV8ValueArray();
final Iterator> iterator = ((BaseStream, ?>) object).iterator();
while (iterator.hasNext()) {
try (V8Value childV8Value = toV8Value(v8Runtime, iterator.next(), depth + 1)) {
v8ValueArray.push(childV8Value);
}
}
v8Value = v8ValueArray;
v8Scope.setEscapable();
}
} else if (object instanceof IJavetEntityFunction) {
final IJavetEntityFunction javetEntityFunction = (IJavetEntityFunction) object;
String sourceCode = javetEntityFunction.getJSFunctionType().isUserDefined() ?
javetEntityFunction.getSourceCode() : null;
if (sourceCode == null || sourceCode.length() == 0) {
v8Value = v8Runtime.createV8ValueNull();
} else {
v8Value = v8Runtime.getExecutor(sourceCode).execute();
}
} else if (object instanceof JavetEntitySymbol) {
final JavetEntitySymbol javetEntitySymbol = (JavetEntitySymbol) object;
v8Value = v8Runtime.createV8ValueSymbol(javetEntitySymbol.getDescription(), true);
} else if (object.getClass().isArray()) {
try (V8Scope v8Scope = v8Runtime.getV8Scope()) {
if (object instanceof boolean[]) {
V8ValueArray v8ValueArray = v8Scope.createV8ValueArray();
for (boolean item : (boolean[]) object) {
v8ValueArray.push(v8Runtime.createV8ValueBoolean(item));
}
v8Value = v8ValueArray;
} else if (object instanceof byte[]) {
byte[] bytes = (byte[]) object;
V8ValueTypedArray v8ValueTypedArray = v8Scope.createV8ValueTypedArray(
V8ValueReferenceType.Int8Array, bytes.length);
v8ValueTypedArray.fromBytes(bytes);
v8Value = v8ValueTypedArray;
} else if (object instanceof char[]) {
V8ValueArray v8ValueArray = v8Scope.createV8ValueArray();
for (char c : (char[]) object) {
v8ValueArray.push(Character.toString(c));
}
v8Value = v8ValueArray;
} else if (object instanceof double[]) {
double[] doubles = (double[]) object;
V8ValueTypedArray v8ValueTypedArray = v8Scope.createV8ValueTypedArray(
V8ValueReferenceType.Float64Array, doubles.length);
v8ValueTypedArray.fromDoubles(doubles);
v8Value = v8ValueTypedArray;
} else if (object instanceof float[]) {
float[] floats = (float[]) object;
V8ValueTypedArray v8ValueTypedArray = v8Scope.createV8ValueTypedArray(
V8ValueReferenceType.Float32Array, floats.length);
v8ValueTypedArray.fromFloats(floats);
v8Value = v8ValueTypedArray;
} else if (object instanceof int[]) {
int[] integers = (int[]) object;
V8ValueTypedArray v8ValueTypedArray = v8Scope.createV8ValueTypedArray(
V8ValueReferenceType.Int32Array, integers.length);
v8ValueTypedArray.fromIntegers(integers);
v8Value = v8ValueTypedArray;
} else if (object instanceof long[]) {
long[] longs = (long[]) object;
V8ValueTypedArray v8ValueTypedArray = v8Scope.createV8ValueTypedArray(
V8ValueReferenceType.BigInt64Array, longs.length);
v8ValueTypedArray.fromLongs(longs);
v8Value = v8ValueTypedArray;
} else if (object instanceof short[]) {
short[] shorts = (short[]) object;
V8ValueTypedArray v8ValueTypedArray = v8Scope.createV8ValueTypedArray(
V8ValueReferenceType.Int16Array, shorts.length);
v8ValueTypedArray.fromShorts(shorts);
v8Value = v8ValueTypedArray;
} else if (object instanceof String[]) {
V8ValueArray v8ValueArray = v8Scope.createV8ValueArray();
for (String item : (String[]) object) {
v8ValueArray.push(v8Runtime.createV8ValueString(item));
}
v8Value = v8ValueArray;
} else {
V8ValueArray v8ValueArray = v8Scope.createV8ValueArray();
for (Object item : (Object[]) object) {
try (V8Value childV8Value = toV8Value(v8Runtime, item, depth + 1)) {
v8ValueArray.push(childV8Value);
}
}
v8Value = v8ValueArray;
}
v8Scope.setEscapable();
}
} else if (!customObjectMap.isEmpty()) {
String customObjectClassName = object.getClass().getName();
Method methodToMap = null;
AccessibleObject[] executables = customObjectMap.get(customObjectClassName);
if (executables != null) {
methodToMap = (Method) executables[EXECUTABLE_INDEX_TO_MAP];
}
if (methodToMap != null) {
try {
Map, ?> map = (Map, ?>) methodToMap.invoke(object);
v8Value = toV8Value(v8Runtime, map);
((V8ValueObject) v8Value).setPrivateProperty(
PRIVATE_PROPERTY_CUSTOM_OBJECT_CLASS_NAME, customObjectClassName);
} catch (Throwable ignored) {
}
}
}
return (T) v8Value;
}
/**
* Unregister custom object.
*
* @param customObjectClass the custom object class
* @return true : success, false: failure
* @since 0.9.12
*/
public boolean unregisterCustomObject(Class> customObjectClass) {
if (customObjectClass == null) {
return false;
}
return customObjectMap.remove(customObjectClass.getName()) != null;
}
}