const time = require('@js-joda/core');
/**
* openHAB JavaScript library version
*
* @memberof utils
* @name OPENHAB_JS_VERSION
* @type {string}
*/
const VERSION = require('../package.json').version;
const log = require('./log')('utils');
const HashSet = Java.type('java.util.HashSet');
const ArrayList = Java.type('java.util.ArrayList');
/**
* Utils namespace.
* This namespace handles utilities, especially for conversion from and to Java data types.
*
* @namespace utils
*/
function _getAllPropertyNames (obj) {
const proto = Object.getPrototypeOf(obj);
const inherited = (proto) ? _getAllPropertyNames(proto) : [];
return [...new Set(Object.getOwnPropertyNames(obj).concat(inherited))];
}
/**
* Convert JavaScript Set to Java Set.
*
* @memberOf utils
* @param {Set} set JavaScript Set
* @returns {JavaSet} Java Set
*/
function jsSetToJavaSet (set) {
const rv = new HashSet();
set.forEach(e => rv.add(e));
return rv;
}
/**
* Convert JavaScript Array to Java Set.
*
* @memberOf utils
* @param {Array} arr
* @returns {JavaSet} Java Set
*/
function jsArrayToJavaSet (arr) {
const set = new HashSet();
for (const i of arr) {
set.add(i);
}
return set;
}
/**
* Convert JavaScript Array to Java List.
*
* @memberOf utils
* @param {Array} arr JavaScript Array
* @returns {JavaList}
*/
function jsArrayToJavaList (arr) {
const list = new ArrayList();
for (const i of arr) {
list.add(i);
}
return list;
}
/**
* Convert Java List to JavaScript Array.
*
* @memberOf utils
* @param {JavaList} list
* @returns {Array} JavaScript Array
*/
function javaListToJsArray (list) {
return Java.from(list);
}
/**
* Convert Java Set to JavaScript Array.
*
* @memberOf utils
* @param {JavaSet} set
* @returns {Array} JavaScript Array
*/
function javaSetToJsArray (set) {
return Java.from(new ArrayList(set));
}
/**
* Convert Java Map to JavaScript Map.
*
* @memberof utils
* @param {JavaMap} map
* @returns {Map<any, any>} JavaScript Map
*/
function javaMapToJsMap (map) {
const js = new Map();
javaSetToJsSet(map.keySet()).forEach((key) => js.set(key, map.get(key)));
return js;
}
/**
* Convert Java Map to JavaScript Object.
*
* @memberof utils
* @param {JavaMap} map
* @returns {object} JavaScript Object
*/
function javaMapToJsObj (map) {
const obj = {};
map.forEach((key, val) => { obj[key] = val; });
return obj;
}
/**
* Convert Java Set to JavaScript Set.
*
* @memberOf utils
* @param {JavaSet} set Java Set ({@link https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Set.html})
* @returns {Set} JavaScript Set Object ({@link https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Set})
*/
function javaSetToJsSet (set) {
return new Set(javaSetToJsArray(set));
}
/**
* Generate a random UUID.
*
* @memberOf utils
* @returns {string} random UUID
*/
const randomUUID = () => Java.type('java.util.UUID').randomUUID();
/**
* Outputs all members and properties of an object and whether it is a JS or a Java Object to the log.
*
* @memberOf utils
* @param {*} obj object
* @param {boolean} [dumpProps=false] whether properties also should be dumped
*/
function dumpObject (obj, dumpProps = false) {
try {
log.info('Dumping object...');
log.info(' typeof obj = {}', (typeof obj));
const isJavaObject = Java.isJavaObject(obj);
log.info(' Java.isJavaObject(obj) = {}', isJavaObject);
const isJavaType = Java.isType(obj);
log.info(' Java.isType(obj) = {}', isJavaType);
if (isJavaObject) {
if (isJavaType) {
log.info(' Java.typeName(obj) = {}', Java.typeName(obj));
} else {
log.info(' Java.typeName(obj.getClass()) = {}', Java.typeName(obj.getClass()));
if (Java.typeName(obj.getClass()) === 'java.util.HashMap') {
log.info('Dumping contents...');
const keys = obj.keySet().toArray();
for (const key in keys) {
log.info('{}({}) = {}({})', keys[key], typeof keys[key], obj.get(keys[key]), typeof obj.get(keys[key]));
if (typeof keys[key] === 'object') {
log.info('Dumping key {} ...', keys[key]);
dumpObject(keys[key]);
}
}
}
}
} else if (typeof obj === 'string') {
log.info(' string value = ' + obj);
} else if (typeof obj === 'boolean') {
log.info(' boolean value = ' + obj);
} else if (typeof obj === 'number') {
log.info(' number value = ' + obj);
} else if (typeof obj === 'object' && obj != null) {
const keys = Object.keys(obj);
log.info(' getOwnPropertyNames(obj) = {}', keys.toString());
log.info(' getAllPropertyNames(obj) = {}', _getAllPropertyNames(obj).toString());
// log.info("obj.toString() = {}", obj.toString());
// log.info("JSON.stringify(obj) = {}", JSON.stringify(obj));
if (dumpProps === true) {
for (const key in keys) {
log.info('Dumping property {} ...', keys[key]);
dumpObject(obj[keys[key]]);
}
}
} else {
log.info(' value = ' + obj);
}
} catch (e) {
log.info('Failed to dump object: ' + e.message);
}
}
/**
* Checks whether an object is instance of a Java class.
*
* @memberOf utils
* @param {*} instance object
* @param {JavaClass} type Java class ({@link https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Class.html})
* @returns {boolean} whether it is an instance of a Java class
* @throws error if type is not a java class
*/
function isJsInstanceOfJavaType (instance, type) {
if (!Java.isType(type)) {
throw Error('type is not a Java type');
}
if (instance === null || instance === undefined || !instance.getClass || !instance.getClass()) {
return false;
}
return Java.typeName(type) === instance.getClass().getName();
}
/**
* Convert Java Instant to JS-Joda Instant.
*
* @memberOf utils
* @param {JavaInstant} instant {@link https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Instant.html java.time.Instant}
* @returns {time.Instant} {@link https://js-joda.github.io/js-joda/class/packages/core/src/Instant.js~Instant.html JS-Joda Instant}
*/
function javaInstantToJsInstant (instant) {
return time.Instant.ofEpochMilli(instant.toEpochMilli());
}
/**
* Convert Java ZonedDateTime to JS-Joda ZonedDateTime.
*
* @memberOf utils
* @param {JavaZonedDateTime} zdt {@link https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/ZonedDateTime.html java.time.ZonedDateTime}
* @returns {time.ZonedDateTime} {@link https://js-joda.github.io/js-joda/class/packages/core/src/ZonedDateTime.js~ZonedDateTime.html JS-Joda ZonedDateTime}
*/
function javaZDTToJsZDT (zdt) {
const epoch = zdt.toInstant().toEpochMilli();
const instant = time.Instant.ofEpochMilli(epoch);
const zone = time.ZoneId.of(zdt.getZone().toString());
return time.ZonedDateTime.ofInstant(instant, zone);
}
/**
* Convert Java ZonedDateTime to JS-Joda ZonedDateTime and default to SYSTEM timezone if not explicitly set.
*
* @memberOf utils
* @param {JavaZonedDateTime} zdt {@link https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/ZonedDateTime.html java.time.ZonedDateTime}
* @returns {time.ZonedDateTime} {@link https://js-joda.github.io/js-joda/class/packages/core/src/ZonedDateTime.js~ZonedDateTime.html JS-Joda ZonedDateTime}
*/
function javaZDTToJsZDTWithDefaultZoneSystem (zdt) {
const jsZDT = javaZDTToJsZDT(zdt);
if (/^[+-]/.test(jsZDT.zone().toString())) {
return jsZDT.withZoneSameLocal(time.ZoneId.SYSTEM);
}
return jsZDT;
}
module.exports = {
jsSetToJavaSet,
jsArrayToJavaSet,
jsArrayToJavaList,
javaListToJsArray,
javaSetToJsArray,
javaSetToJsSet,
javaMapToJsMap,
javaMapToJsObj,
randomUUID,
dumpObject,
isJsInstanceOfJavaType,
javaInstantToJsInstant,
javaZDTToJsZDT,
javaZDTToJsZDTWithDefaultZoneSystem,
OPENHAB_JS_VERSION: VERSION
};