I have this engine which runs user-input (trusted) javascript function to filter some data for them using Nashorn. Don't want to go into the details of the requirements but let's say it's some sort of plugin system.
This javascript gets a Java map(which acts like a JS object) as a parameter to get some contextual data.
So I want to add some convenience extension methods to this JS object for the users. So I did the following:
Map<String, String> inputMap = ....
inputMap.put("containsMagic", (Function<String, Boolean>) param -> some complex magic check and return boolean);
Works perfectly!
if(inputMap.containsMagic('Im Por Ylem'))
do stuff;
However, I want it to accept no parameter as well, because null is a valid value and containsMagic() looks better than containsMagic(null).
if(inputMap.containsMagic())
do stuff;
But I get this:
TypeError: Can not invoke method ......$$Lambda$15/1932831450#30c15d8b with the passed arguments; they do not match any of its method signatures.
I guess that's kind of normal given how Java works. If I pass null, it works of course but that's not very intuitive.
I can't add another function with 0 parameters and the same name because Java maps are single keyed. The complex magic check needs to be in java, so I can't assign a javascript function (JSObject?) either. Any ideas how I can accomplish this?
You can implement containsMagic as explicit method with a varargs parameter:
public static class InputMap extends HashMap<String, Object> {
public Boolean containsMagic(String... args) {
String arg = args.length == 1 ? args[0] : null;
// do magic
}
}
...
Map<String, Object> inputMap = new InputMap();
java.util.function unfortunately lacks a standard interface for variadic functions, but you could add your own:
#FunctionalInterface
public interface VariadicFunction<T,R> {
public R apply(T... args);
}
and then
inputMap.put("containsMagic", (VariadicFunction<String, Boolean>) param -> some complex magic check and return boolean);
Related
I'm trying to extend a Java class in a JavaScript project using ES4X/Graal. The class that I want to extend, has methods with overloeaded parameters that I need to override. I know that you can call a specific Java method by using the square brackets notation and specifying the type (example below), but, apparently, there's no way to specify the parameter types when overriding, per the Graal/Oracle/Nashorn documentation. So if you have the following:
package com.mycomp;
class A {
public void method(String parameter) {...}
public void method(int parameter) {...}
}
you can call either method in JavaScript like so:
var a = Java.type("com.mycomp.A");
a["method(String)"]("Some string parameter")
a["method(int)"](42)
a.method(stringOrIntParam)
However, when extending, you can only do the following:
var ClassAFromJava = Java.type("com.mycom.A");
var MyJavaScriptClassA = Java.extend(ClassAFromJava, {
method: function(stringOrIntParam) {...}
}
I want to be able to extend only one of the method(...), um, methods. And what about overloaded methods with different return types?
Thanks!
The sj4js allows you to do all that.
This example is slightly modified from the documentation...
public class TestObject extends JsProxyObject {
// the constructor with the property
public TestObject (String name) {
super ();
this.name = name;
// we hvae to initialize the proxy object
// with all properties of this object
init(this);
}
// this is a mandatory override,
// the proxy object needs this method
// to generate new objects if necessary
#Override
protected Object newInstance() {
return new TestClass(this.name);
}
// a method with a String parameter
public String method (String s) {
return "String";
}
// a method with a int parameter
public String method (int i) {
return "42";
}
}
And you can access this object as you would access a JS object and
the library takes care of selecting the appropriate method.
try (JScriptEngine engine = new JScriptEngine()) {
engine.addObject("test", new TestClass("123"));
// calling the method with the String parameter would
// result in calling the appropriate java method
engine.exec("test.method('123')");
// returns "String"
// calling the method with the int parameter would
// result in calling the appropriate java method
engine.exec("test.method(123)");
// returns "42"
}
I'm using Spring Data JPA (with Hibernate as my JPA provider) and want to define an exists method with a HQL query attached:
public interface MyEntityRepository extends CrudRepository<MyEntity, String> {
#Query("select count(e) from MyEntity e where ...")
public boolean existsIfBlaBla(#Param("id") String id);
}
When I run this query, I get a java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.Boolean.
How does the HQL query have to look like to make this work? I know I could simply return a Long value and afterwards check in my Java code if count > 0, but that workaround shouldn't be necessary, right?
Spring Data JPA 1.11 now supports the exists projection in repository query derivation.
See documentation here.
In your case the following will work:
public interface MyEntityRepository extends CrudRepository<MyEntity, String> {
boolean existsByFoo(String foo);
}
I think you can simply change the query to return boolean as
#Query("select count(e)>0 from MyEntity e where ...")
PS:
If you are checking exists based on Primary key value CrudRepository already have exists(id) method.
in my case it didn't work like following
#Query("select count(e)>0 from MyEntity e where ...")
You can return it as boolean value with following
#Query(value = "SELECT CASE WHEN count(pl)> 0 THEN true ELSE false END FROM PostboxLabel pl ...")
It's gotten a lot easier these days!
#Repository
public interface PageRepository extends JpaRepository<Page, UUID> {
Boolean existsByName(String name); //Checks if there are any records by name
Boolean existsBy(); // Checks if there are any records whatsoever
}
Since Spring data 1.12 you can use the query by Example functionnality by extending the QueryByExampleExecutor interface (The JpaRepositoryalready extends it).
Then you can use this query (among others) :
<S extends T> boolean exists(Example<S> example);
Consider an entity MyEntity which as a property name, you want to know if an entity with that name exists, ignoring case, then the call to this method can look like this :
//The ExampleMatcher is immutable and can be static I think
ExampleMatcher NAME_MATCHER = ExampleMatcher.matching()
.withMatcher("name", GenericPropertyMatchers.ignoreCase());
Example<MyEntity> example = Example.<MyEntity>of(new MyEntity("example name"), NAME_MATCHER);
boolean exists = myEntityRepository.exists(example);
Apart from the accepted answer, I'm suggesting another alternative.
Use QueryDSL, create a predicate and use the exists() method that accepts a predicate and returns Boolean.
One advantage with QueryDSL is you can use the predicate for complicated where clauses.
You can use Case expression for returning a boolean in your select query like below.
#Query("SELECT CASE WHEN count(e) > 0 THEN true ELSE false END FROM MyEntity e where e.my_column = ?1")
Spring data provides method for checking the existence of a row using field:
example: boolean existsByEmployeeIdAndEmployeeName(String employeeId, String employeeName);
You can use .exists (return boolean) in jpaRepository.
if(commercialRuleMsisdnRepo.exists(commercialRuleMsisdn.getRuleId())!=true){
jsRespon.setStatusDescription("SUCCESS ADD TO DB");
}else{
jsRespon.setStatusCode("ID already exists is database");
}
I'm wondering is there a way of casting a Java object in Rhino? I would usually do this in Java by doing the following:
List<Object[]> mylist = myServiceClass.getList(id);
for (Object[] object : myList) {
Apple a = (Apple) object[0];
// do something
}
But in Rhino, I'm not sure. Is there a way to do this?
I've tried:
apple = myList.get(i)
And:
apple = (Packages.com.package.fruits.Apple) myList.get(i);
I keep getting the error
Java class "[Ljava.lang.Object;" has no public instance field or method named "getColour" when trying to access the apple object.
You don't need to cast in javascript. But you need to call the method on the correct object. The error message states that you call the method on an Object[] (The [ before L gives that away).
To get the first object from Object[] before you call getColour() on it
myList.get(i)[0].getColour()
Is there a shorter way to write this in c#:
if(myobject!=null){
}
In JavaScript we can do this:
if(myobject){
}
Disclaimer: I know this will match 'true' as well in JavaScript. This would only be used on variables that should be a specific type of object.
I found some similar questions, but they're asking slightly different things:
C# Shortest Way to Check for Null and Assign Another Value if Not
Best and fastest way to check if an object is null
How to determine if variable is 'undefined' or 'null'?
You can obtain the same syntax in C# via operator:
public class MyClass {
...
// True if instance is not null, false otherwise
public static implicit operator Boolean(MyClass value) {
return !Object.ReferenceEquals(null, value);
}
}
....
MyClass myobject = new MyClass();
...
if (myobject) { // <- Same as in JavaScript
...
}
C# language philosophy is quite different than that of JavaScript. C# usually forces you to be more explicit about somethings in order to prevent some common programming errors (and I'm sure this also helps simplify the compiler design & test).
If C# had allowed such an implicit conversion to boolean, you are much more likely to run into programming errors like this:
if(myobject = otherObject)
{
...
}
where you've made an assignment instead of an equality check. Normally C# prevents such mistakes (so although Dmitry's answer is clever, I'd advise against it).
used ?? Operator https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-conditional-operator
var myobject = notNullValue ?? nullValue;
If you want to Throw an ArgumentNullException e.g. to check method parameters, there is a handy one-liner to do this:
_ = x ?? throw new ArgumentNullException(nameof(x));
Here, we try to assign the parameter to the discard _ operator. The ??-operator performs a nullcheck. If the variable is null, the exception is thrown.
In Visual Studio you can add a custom snippet to bind this line to the shortcut arg0. You only need to type arg0, double press the TAB key, and to type the parameter name. Implementing a null check then only takes 2 seconds.
Here is the snippet. To import it into Visual Studio, please use this guide: https://learn.microsoft.com/de-de/visualstudio/ide/walkthrough-creating-a-code-snippet?view=vs-2019
<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>Argument null check for parameters</Title>
<Shortcut>arg0</Shortcut>
</Header>
<Snippet>
<Code Language="CSharp">
<![CDATA[_= $param$ ?? throw new ArgumentNullException(nameof($param$));]]>
</Code>
<Declarations>
<Literal>
<ID>param</ID>
<ToolTip>Name of the parameter.</ToolTip>
<Default>x</Default>
</Literal>
</Declarations>
</Snippet>
</CodeSnippet>
You can use object class static method ReferenceEquals method to find out if the refrence is null or not
MyClass1 obj = new MyClass1();
if (object.ReferenceEquals(obj,null))
{
Console.Write("obj ref is null");
}
I'm trying to integrate Mixpanel with GWT, but I have problems calling an event with a property and one value.
My function to track an simple event (without values):
public native void trackEvent(String eventName)/*-{
$wnd.mixpanel.track(eventName);
}-*/;
It works.
But when I want to add some properties and values, it doesn't work properly:
public native void trackComplexEvent(String eventName, String property,
String value)/*-{
$wnd.mixpanel.track(eventName, {property:value});
}-*/;
I have 2 problems with this:
1) Mixpanel says the property name is: "property"(yes, the name of the variable that I'm passing, not the value).
2) Mixpanel says the value is:undefined
An example from mixpanel web is:
mixpanel.track("Video Play", {"age": 13, "gender": "male"});
So, I guess the problem is I'm doing a wrong call or with wrong type of arguments.
your problem is, that when you pass the you create the property object, you don't create a parameter, taken from your property name, but rather a property named property. If you debug your code, you can check, that a parameter property is passed to your mixpanel.track function.
To do what you want, you have to use an other syntax.
public native void trackComplexEvent(String eventName, String property,
String value)/*-{
//create the property object you want to pass
var propertyPassed = {}
// set the value you want to pass on the propertyPassed object
propertyPassed[property] = value;
//call your function with the argumetn you want to pass
$wnd.mixpanel.track(eventName, propertyPassed);
}-*/;
I tested your code, by creating a JavaScript funciton on my site:
window.mixpanel = {
track : function(eventName, props) {
alert(props.age);
}
}
and calling it with
trackComplexEvent("hallo", "age", "13");
The alert was '13'
BR,
Stefan