Possibility to call nonstatic gwt java method from javascript - java

General Aim: call some nonstatic java method from javascript
Description:
To find gwt's widget on DOM in Java Applet code and call it's java method(non static)
JSObject win = JSObject.getWindow(this);
JSObject doc = (JSObject) win.getMember("document");
JSObject gwtWidget = (JSObject) doc.call("getElementsByName", widgetName);
//todo: have possibility to call `exported` java method over here, smth like:
//Object[] params = new Object[1];
//params[0] = widgetName;
//Object result = gwtWidget.call("exportedJavaMethod", params);
//todo: or just call global ($wnd) static JSNI bridge method:
//Object result = win.call("exportedJavaMethod", params);
//
The problem is: I can find by widget's id not the widget, but it's DivElement which does not have any exported instanced methods.
My widget class is Exportable (gwt-export):
#Export(value="somewidget")
public class SomeWidget extends SimplePanel implements ..., Exportable {
private final String id = "id_some_widget_" + count++;
private static int count = 0;
public SomeWidget () {
getElement().setAttribute("name", id);
getElement().setId(id);
}
...
public static Element getInstanceById(String elementId) {
Element someWidget= DOM.getElementById(elementId);
return someWidget;
}
public String getSomeInstancedData() {
return "useful_inner_data" + this.id;
}
So, for example I'd like to find the concrete widget added to DOM and call nonstatic method getSomeInstancedData() in javascript. Is it possible at all?
Suppose smth like:
var someWidget = document.getElementById(widgetId);
alert(someWidget.getSomeInstancedData());
//or:
var someWidgetExported = com.mypackage.widgets.somewidget.getInstanceById(listenerId);
alert(someWidgetExported.getSomeInstancedData());
In Base module I write:
ExporterUtil.exportAll();
There is a View(ViewWithSomeWidget.ui.xml) that contains this widget:
...
base:Form
base:SomeWidget ui:field="someWidget" ...
...
/base:SomeWidget
...
When SomeWidget does not implement Exportable, project runs well, but I couldn't call nonstatic methods of found widget's DIV element.
By the time, to solve the problem SomeWidget implements Exportable, but progect doesn't show View with the SomeWidget well because of ClassCastException using deferred binding:
ClassCastException: com.mypackage.widgets.SomeWidgetExporterImpl cannot be cast to com.mypackage.widgets.SomeWidget
So, probably there are any other methods to find widget's javascript object and call it's exported java method? In any ways, any idea is much appreciated.

You can declare a javascript function, which will fire the non-static method.
For example:
package com.example;
public class Layout extends VLayout() {
private String nonStaticVar = "nonStaticVar";
public Layout() {
declareMethod(this);
}
//Method which declares non-static method in javascript
public native void declareMethod(Layout layout) /*-{
var layout = layout;
$wnd.doSomething = function(someString) {
layout.#com.example.Layout::doSomething(Ljava/lang/String;)(someString);
}
}-*/;
//Non static method which is being called
public String doSomething(String someString) {
//Do something, e.g. set var in this instantiated class, or output stuff
this.nonStaticVar = someString;
Window.alert(someString);
}
}
Now, calling
doSomething("bla");
from javascript will call your non-static method.

you can define an own jsmethod on the div element, that can call a widget-method
#Export(value="somewidget")
public class SomeWidget extends SimplePanel implements ..., Exportable {
private final String id = "id_some_widget_" + count++;
private static int count = 0;
public SomeWidget () {
getElement().setAttribute("name", id);
getElement().setId(id);
attachInstanceHook(getElement());
}
...
public String getSomeInstancedData() {
return "useful_inner_data" + this.id;
}
private native void attachInstanceHook(Element element) /*-{
var elem = element;
var widget = this;
elem.getWidgetData = function () {
widget.#your.package.name.SomeWidget::getSomeInstancedData()();
};
}-*/;
and your js-code should look like this:
var someWidget = document.getElementById(widgetId); //the id of the div element
alert(someWidget.getWidgetData());

instead of trying to fetch the widget through its DOM element, inject its Java instance first to the global scope and refer to it:
public class SomeWidget extends SimplePanel implements ..., Exportable {
//...
public SomeWidget () {
getElement().setAttribute("name", id);
getElement().setId(id);
attachToGlobalScope(id, this);
}
private native void attachToGlobalScope(String id, SomeWidget instance) /*-{
$wnd.shared[id] = instance;
}-*/;
//...
}
than later, update the corresponding fetch in the Applet API layer:
JSObject win = JSObject.getWindow(this);
JSObject shared = (JSObject) win.getMember("shared");
Widget widgetRef = (Widget) shared.getMember(widgetId);
disclaimer: this somewhat crooked solution relays on JavaScript global scope variables, which is considered bad practice in general, but here acts as the only common scope, thus abused for objects storage.

Related

Want to use Enum file name dynamically to fetch constants

I have a config file with key value pair as
language = "IN"
and i have multiple page object enum files with name as
PageObject_US,PageObject_UK,PageObject_IN
every page object enum file has constants that can be accessed using for example
PageObjects_US.String.lable
but what i want to achieve is a way to create something like below
take the parameter from config file store it in a string
like String language = "IN"
Then concatenate using "PageObjects_" + language to get (PageObjects_IN)
so that the returned value can be used to fetch the constants from PageObjects_IN.String.label.
following is the code block:
if(!ENV.equalsIgnoreCase("development") && VALIDATION.equalsIgnoreCase("yes")) {
Elements.ByTitle(webDriver,PageObjects_IN.GREAT.label);
Elements.ByID(webDriver, PageObjects_IN.COUNTER.label);
}
In the above i want to use enum file PageObjects_IN at run time as i have many enum files
below is the enum
public enum PageObjects_IN {
// Text
GREAT("great"),
COUNTER("counter");
public final String lable;
PageObjects_IN(final String lable) {
this.lable = lable;
}
}
This is possible (using reflection) but strongly not recommended as it eliminates the efficiency of Java language constructs.
Not recommended way
Say you have a package click.webelement.cucumber.po where you store
public enum PO_EN {
GREAT("great_en"),
COUNTER("counter_en");
public final String label;
PO_EN(String label){
this.label = label;
}
}
and
public enum PO_IN {
GREAT("great_in"),
COUNTER("counter_in");
public final String label;
PO_IN(String label){
this.label = label;
}
}
Then to take a value you can do something like this:
String lang = "EN";
// Take class
Class clazz = Class.forName("click.webelement.cucumber.po.PO_" + lang);
// Find an object that represent enum constant
Object val = Arrays
.stream(clazz.getEnumConstants()).filter(o -> "GREAT".equals(o.toString()))
.findAny()
.get();
// Take field value for that object
Field f = clazz.getField("label");
System.out.println(f.get(val));
This is error-prone approach and you would not have benefit from compile phase.
Recommended approach - 1
Instead of having enum use classes.
public abstract class PO {
public abstract String great();
public abstract String counter();
}
and
public class PO_EN extends PO{
#Override
public String great() {
return "great_en";
}
#Override
public String counter() {
return "counter_en";
}
}
and
public class PO_IN extends PO{
#Override
public String great() {
return "great_in";
}
#Override
public String counter() {
return "counter_in";
}
}
so your test would be much simpler
String lang = "EN";
Class clazz = Class.forName("click.webelement.cucumber.po.PO_" + lang);
PO val = (PO) clazz.getDeclaredConstructor().newInstance();
System.out.println(val.great());
Recommended approach - 2
You can utilize PageFactory harness for your page objects and use this lib to parametrize your locators, like (if you use test ng):
#DataProvider(name = "languages")
Object[][] dataProvider(){
return new Object[][]{
{"en", "great_en", "counter_en"},
{"in", "great_in", "counter_in"}
};
}
#Test(dataProvider = "languages")
public void testPage(String language, String great, String counter){
DefaultParameterProvider
.properties
.set(Map.of("p.great", great, "p.counter", counter));
MyPage myPage = new MyPage(driver);
...
}
Where your page would be like this:
public class MyPage extends PageObjectParameterized {
#FindByParameterized(xpath = "//button[#name='{wec:p.great}']")
WebElement great;
#FindByParameterized(xpath = "//label[text()='{wec:p.counter}']")
WebElement counter;
#FindBy(xpath = "//input")
WebElement input;
public MyPage(SearchContext searchContext) {
super(searchContext);
}
}

Pass new object with the use of setter in method parameters

I'm trying to pass new Object as method parameter and set it's variable at the same time.
This an okay solution but big and not nice...
EventBox evtbox = new EventBox();
evtbox.setFloorColor(floorColor);
scriptUtils.runScript("sc-cfrmd",evtbox);
and I need something like this to shorten it up a bit
scriptUtils.runScript("sc-cfrmd",new EventBox().setFloorColor(floorColor));
of course, logical way of doing this is just creating a constructor in EventBox class but I need to figure out way without use of constructors.
any tips are appreciated
You are looking for fluent interface
Usually, when you need to create a complex object you are implementing fluent interface with builder design pattern
For example:
import java.awt.Color;
public class EventBox {
private Color floorColor;
private EventBox() {
this.floorColor = null;
}
public Color getFloorColor() {
return floorColor;
}
private void setFloorColor(Color floorColor) {
this.floorColor = floorColor;
}
public static EventBoxBuilder builder() {
return new EventBoxBuilder();
}
public static class EventBoxBuilder {
private final EventBox box;
EventBoxBuilder() {
this.box = new EventBox();
}
public EventBoxBuilder setFloorColor(Color color) {
box.setFloorColor(color);
return this;
}
public EventBox build() {
return box;
}
}
}
....
scriptUtils.runScript("sc-cfrmd",EventBox.builder().setFloorColor(floorColor).build());
If you are able to use Lombok Framwork, such builder can be automatically generated on compile time by adding #Builder annotation to the EventBox class
You can use method chaining by adding methods as desired. Conventionally, leave the setters/getters as the standard practice to just do what their name says.
public EventBox withFloorColor(String floorColor) {
setFloorColor(floorColor);
return this;
}
scriptUtils.runScript("sc-cfrmd",new EventBox().withFloorColor(floorColor));
The most basic and simple solution is of course to create a method like
EventBox createFloorEventBox( String floorColor ) {
EventBox eb = new EventBox();
eb.setFloorColor( floorColor );
return eb;
}
and use it like
scriptUtils.runScript("sc-cfrmd", createEventBox( floorColor ) );
Besides the builder pattern/method chaining/fluent approach, you could also consider using lambdas in Java, like
void runScript(String something, Consumer<EventBox> boxInitializer) {
EventBox eb = new EventBox();
initializer.apply(eb);
...
}
and then call this like
runScript("something", eb -> eb.setFloorColor( floorColor ));

Passing variable set in one Jframe from one to another

I have one JFrame called User where I declare a variable called id and set it to a certain value depending on some conditions.
I need to use this variable in a second JFrame called output.
This is the code I have
class InputScreen extends javax.swing.JFrame {
public int id = 0;
private void submitButtonActionPerformed(java.awt.event.ActionEvent evt) {
if (condition){
id = 1;
System.out.println(id);
}
elseif (condition){
id = 2;
System.out.println(id);
}
elseif (condition){
id = 3;
System.out.println(id);
}
else{
System.exit(0);
}
I used a constructor in the frame Output but it doesn't seem to work.
public class Output extends javax.swing.JFrame {
int rule;
public Output(int Id){
rule = Id;
initComponents();
}
public Output() {
initComponents();
conn = MySqlConnect.ConnectDB();
}
Updated Code
Frame - Input
class InputScreen extends javax.swing.JFrame {
public int id = 0;
public int getID(){
return input_rule_id;
}
private void submitButtonActionPerformed(java.awt.event.ActionEvent evt) {
if (condition){
id = 1;
System.out.println(id);
}
elseif (condition){
id = 2;
System.out.println(id);
}
elseif (condition){
id = 3;
System.out.println(id);
}
Form - Output
private void formWindowActivated(java.awt.event.WindowEvent evt) {
Input InSc = new Input();
InSc.getId();
}
All primitive data-types will be passed by the value. Use an object wrapper to pass the value by the reference. For example AtomicInteger
class InputScreen extends javax.swing.JFrame {
private AtomicInteger id = new AtomicInteger(0);
private void submitButtonActionPerformed(java.awt.event.ActionEvent evt) {
if (condition){
id.set(1);
System.out.println(id);
}
else if (condition){
id.set(2);
System.out.println(id);
}
else if (condition){
id.set(3);
System.out.println(id);
}
else{
System.exit(0);
}
}
public class Output extends javax.swing.JFrame {
AtomicInteger rule;
public Output(AtomicInteger Id){
rule = Id;
initComponents();
}
public Output() {
initComponents();
conn = MySqlConnect.ConnectDB();
}
}
Parameter passing
The most simple way would be passing Output as a parameter to InputScreen. Then you might just call a method Output.setId(id) within your InputScreen.submitButtonActionPerformed() logic. This way however is a bit problematic, when the application grows.
Observer Pattern
A better way would be realizing a IdChangedListener as part of an Observer pattern as a proxy to the ActionListener you already have implemented. When your ActionListener fires, you call all IdChangeListeners that have registered as Observers to InputScreen. In Output you provide a method setId(id) to provide access. In the place you instantiate the two JFrames you implement an IdChangeListener, add it to InputScreen and when it fires you call Output.setId().
Anyway, you see a lot of work (and code) for just one event. Also this approach has its limitations concerning application size and dynamic.
In Memory EventBus
The up-to-date approach would be using a in memory EventBus. This eliminates the hard-wiring of the components within your UI and reduces the code size. However, it limits the reuse of components because they will be automatically react to some events. What if you have them more than once? Listening to different Events?
You need to think about where you want to use which approach. I would suggest using an EventBus for event propagation between the concrete components of your application. Use Observer pattern on medium size components while use parameter passing for small size or very static ones.
You can use getters and setter. Define a getter method in your Frame from where you want to pass the id variable. eg
public int getID(){
return id;
}
In your other frame you can just use this method to return the result. int id = getID();
Another work around for this problem is making the variable globe and static so it can be imported in the other frame and be used.

Restrict access to only necessary data-class accessors / getters

I have a data class that is created in one class, and can be passed to one of several Android UI Activities that implement a specific Interface.
Each UI uses the same data, however, not all the UIs need all of the data. I was wondering if there was a simple solution that allows each UI to only use a specific part of the data (only use specific accessors / getters)
ClickListener Handler Class
// ICalculatorAbstraction is what all my UI's implement. It has method... void updateResult(ExpressionState expression)
public final View.OnClickListener listenerOn(final ICalculatorAbstraction UI) {
return listener = new View.OnClickListener() {
#Override
public void onClick(View v) {
// Calculations
ExpressionState expression = new ExpressionState.Builder()
.setFirstNumber(num1)
.setSecondNumber(num2)
.setTotal(total)
.setOperator(operator.getSign())
.build();
UI.updateResult(expression);
}
};
}
ICalculatorAbstraction Interface
Again, all of my Android Activities (UIs) implement this interface
public interface ICalculatorAbstraction {
...
void updateResult(ExpressionState result);
}
Needs All Accessors UI
#Override
public void updateResult(ExpressionState result) {
String results = String.format( // NOTE: this one needs ALL the accessors / getters!!
"%s %s %s = %s",
result.getFirstNumber(),
result.getOperator(),
result.getSecondNumber(),
result.getTotal()
);
this.txtResult.setText(results);
Needs One Accessor UI
#Override
public void updateResult(ExpressionState result) {
String results = String.format( // NOTE: this one needs ONE accessor / getter!!
"Wow. Such Calcuation. %s",
result.getTotal()
// NOTE: These should not be allowed in this instance because this UI does not use them
// result.getFirstNumber()
// result.getOperator()
// result.getSecondNumber()
);
this.txtResult.setText(results);
How can I change updateResult(...) (both in the interface and in the UI) so that the specific UI's updateResult(...) will only let me use the needed assessors / getters?
I have tried to create a blank interface, and then created 2 abstract classes that implement that blank interface. The abstract classes had only the accessors / getters I needed, but when I tried to modify the above code, nothing worked.
Is this possible?
Update
Here is what I would like to see - "my best possible solution" you can say.
Needs All Accessors UI
#Override
public void updateResult(IAllAccessors result) {
String results = String.format(
"%s %s %s = %s",
result.getFirstNumber(),
result.getOperator(),
result.getSecondNumber(),
result.getTotal()
);
this.txtResult.setText(results);
Needs One Accessor UI
#Override
public void updateResult(IOneAccessorOnly result) {
String results = String.format(
"Wow. Such Calcuation. %s",
result.getTotal() // I should not be able to do result.getFirstNumber(); for example
);
this.txtResult.setText(results);
ExpressionState / Builder Class
public class ExpressionState implements IOneAccessorOnly, IAllAccessors {
private ExpressionState(Builder builder) { ... }
public double getFirstNumber() { ... } // IAllAccessors
public double getSecondNumber() { ... } // IAllAccessors
public String getOperator() { ... } // IAllAccessors
public double getTotal() { ... } // IAllAccessors, IOneAccessorOnly
public static class Builder { ... }
}
The problem with this solution, is that I cannot figure out what to do with my interface that I have above! I cannot have a parameter that will make the compiler happy with both UIs.
Update 2
I cannot have...
In my ClickListener class when creating ExpressionState with the builder
IOneAccessorOnly var = new ExperssionState.Builder()...
This is because in my ClickListener class, I don't know which one to create. It has to be very generic. In my UI's I want to simplify what I can use. I cannot do that with this approach
Because it does not know what to be, it has to be "everything"
ExpressionState var = new ExpressionState.Builder()...
It really cannot be anything other than that. The solution will have to deal with the UIs (Activities) specifically to narrow down what is allowed!!
If Expression state is your own class I'd make it implement two different interfaces like so.
public class ExpressionState implements InterfaceOne, InterfaceTwo {
public void interfaceOneGetterMethod()
public void interfaceTwoGetterMethod()
}
in another file:
public interface InterfaceOne {
public void interfaceOneGetterMethod();
and final file:
public interface InterfaceTwo {
public void interfaceTwoGetterMethod();
Now when you create the ExpressionState objects assign them to objects defined as:
InterfaceTwo var = new ExperssionState(blah);
Or modify your builder to just return interfaces( even better )

Java Builder Pattern pass to session JSP servlet

I just started learning and implementing builder patterns from Wiki. And also CH2 of Effective Java.
This pertains to JSP servlets, this might be a little convoluted, but I just wanted to pass this by you guys to see how to do this correctly.
Lets start with a scenario, where you can't build the object completely there are certain information that is not given. So most likely you have to put the object in session and then add variables to the session object later. How would I accomplish this with Build pattern?
Here is a code example
public class Widget(){
public static class Builder(){
public Builder(String id) {...}
public Builder serialNumber (String val) {...}
public Builder area (String val) {...}
public Widget build() { return new Widget(this); }
}
private Widget(Builder builder){...}
}
JSP Servlet 1 // only have ID information
Widget w = new Widget().Builder(id).build();
HttpSession session = request.getSession();
session.setAttribute("widget", w);
JSP Servlet 2 // Now I have serial and area
Widget.Builder w = (Widget.Builder) session.getAttribute("widget");
//** This is as far as I go **
w.serialNumber("something") // Now this works
.area("sideArea") //
.build() // < I know if I do this I will be creating another Object. Is there a way to use build pattern without creating redundant obj?
Thank you all...
w.serialNumber("something") // Can not find symbol
because serialNumber is not a method of the w object. What you're probably looking for is method chaining:
public class Widget {
... //assuming you have all the right fields here
public Widget setSerialNumber(String id) {
this.id = id;
return this;
}
public Widget setArea(String area) {
this.area = area;
return this;
}
public static class Builder(){
public Builder(String id) {...}
public Builder serialNumber (String val) {...}
public Builder area (String val) {...}
public Widget build() { return new Widget(this); }
}
private Widget(Builder builder){...}
}
then you can do something like this:
w.setSerialNumber(id).setArea(area);
Use Widget.Builder when you want to construct a new Widget object, and method chaining when you want to change an existing Widget object.

Categories