Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhances JNetReflector to manage classes that needs to create Callbacks in .NET #512

Closed
masesdevelopers opened this issue Aug 4, 2024 · 5 comments · Fixed by #525, #527, #528, #529 or #531
Closed
Assignees
Labels
enhancement New feature or request java Pull requests that update Java code JNetReflector JNetReflector related issue .NET Pull requests that update .net code

Comments

@masesdevelopers
Copy link
Contributor

masesdevelopers commented Aug 4, 2024

From the point of view of .NET code in Netdroid, the 'OnCreate' shall be managed like an event of a listener. In this way the .NET code can react and execute the needed code like

https://github.com/masesgroup/netdroid/blob/84d980756a94c0350b6bbb787d530be6d98d27bf/src/net/Netdroid/Generated/Android/App/Activity.cs#L1957
that shall be an invocable method.

There are two possible alternatives to create some classes that needs the behavior requested:

  • manually develops the behavior using some new classes defined in JVM side
  • request an enhancement to JNetReflector

Originally posted by @masesdevelopers in masesgroup/netdroid#75 (comment)

@masesdevelopers masesdevelopers self-assigned this Aug 4, 2024
@masesdevelopers masesdevelopers added enhancement New feature or request JNetReflector JNetReflector related issue .NET Pull requests that update .net code java Pull requests that update Java code labels Aug 4, 2024
@masesdevelopers
Copy link
Contributor Author

More or less the same behavior shall be applied to the classes ending with Adapter. Consider the WindowAdapter class from OpenJDK:

public abstract class WindowAdapter
    implements WindowListener, WindowStateListener, WindowFocusListener
{
    /**
     * Constructs a {@code WindowAdapter}.
     */
    protected WindowAdapter() {}

/// other methods

    /**
     * Invoked when a window is activated.
     */
    public void windowActivated(WindowEvent e) {}

/// other methods
}

the class is managed in JNet with the following JVM WindowAdapter snippet:

public final class WindowAdapter extends org.mases.jcobridge.JCListener implements java.awt.event.WindowListener, java.awt.event.WindowStateListener, java.awt.event.WindowFocusListener {
    public WindowAdapter(String key) throws org.mases.jcobridge.JCNativeException {
        super(key);
    }

    //@Override
    public void windowActivated(java.awt.event.WindowEvent arg0) {
        raiseEvent("windowActivated", arg0);
    }

/// other methods
}

and the following .NET WindowAdapter class:

    public partial class WindowAdapter : MASES.JCOBridge.C2JBridge.JVMBridgeListener
    {
        /// <summary>
        /// Default constructor: even if the corresponding Java class does not have one, it is mandatory for JCOBridge
        /// </summary>
        public WindowAdapter() { InitializeHandlers(); }

        const string _bridgeClassName = "org.mases.jnet.generated.java.awt.event.WindowAdapter";
        private static readonly IJavaType _LocalBridgeClazz = ClazzOf(_bridgeClassName);
        private static IJavaType LocalBridgeClazz => _LocalBridgeClazz ?? throw new InvalidOperationException($"Class {_bridgeClassName} was not found.");
        
        /// <summary>
        /// <see href="https://www.jcobridge.com/api-clr/html/P_MASES_JCOBridge_C2JBridge_JVMBridgeListener_BridgeClassName.htm"/>
        /// </summary>
        public override string BridgeClassName => _bridgeClassName;

        #region Instance methods
        /// <summary>
        /// Handlers initializer for <see cref="WindowAdapter"/>
        /// </summary>
        protected virtual void InitializeHandlers()
        {
            AddEventHandler("windowActivated", new global::System.EventHandler<CLRListenerEventArgs<CLREventData<Java.Awt.EventNs.WindowEvent>>>(WindowActivatedEventHandler));

/// other invocations
        }

        /// <summary>
        /// Handler for <see href="https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/java/awt/event/WindowAdapter.html#windowActivated(java.awt.event.WindowEvent)"/>
        /// </summary>
        /// <remarks>If <see cref="OnWindowActivated"/> has a value it takes precedence over corresponding class method</remarks>
        public global::System.Action<Java.Awt.EventNs.WindowEvent> OnWindowActivated { get; set; } = null;

        void WindowActivatedEventHandler(object sender, CLRListenerEventArgs<CLREventData<Java.Awt.EventNs.WindowEvent>> data)
        {
            var methodToExecute = (OnWindowActivated != null) ? OnWindowActivated : WindowActivated;
            methodToExecute.Invoke(data.EventData.TypedEventData);
        }

        /// <summary>
        /// <see href="https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/java/awt/event/WindowAdapter.html#windowActivated(java.awt.event.WindowEvent)"/>
        /// </summary>
        /// <param name="arg0"><see cref="Java.Awt.EventNs.WindowEvent"/></param>
        public virtual void WindowActivated(Java.Awt.EventNs.WindowEvent arg0)
        {
            
        }
/// other methods
        #endregion

    }

In case of Adapters, or class shall execute callbacks on .NET, the JNetReflector tool shall create a new kind of JVM class that avoids to extend org.mases.jcobridge.JCListener but implements org.mases.jcobridge.IJCListener:

public final class WindowAdapter extends java.awt.event.WindowAdapter implements org.mases.jcobridge.IJCListener {
   org.mases.jcobridge.JCListener _jumper;

    public WindowAdapter(String key) throws org.mases.jcobridge.JCNativeException {
        super();
        _jumper = new org.mases.jcobridge.JCListener(key);
    }

   protected void finalize() throws Throwable {
      super.finalize();
      _jumper.finalize();
   }

   /**
    * Invoked when the CLR counterpart request to release the instance.
    */
   public synchronized void release() {
      _jumper.release();
   }

/// all other methods of org.mases.jcobridge.IJCListener

    //@Override
    public void windowActivated(java.awt.event.WindowEvent arg0) {
         org.mases.jnet.EventResult executionResult = new org.mases.jnet.EventResult();
        _jumper.raiseEvent("windowActivated", executionResult, arg0);
        if (!executionResult.hasImplementation()) {
            super.windowActivated(arg0);
        }
    }

/// other methods
}

from the snippet above a new org.mases.jnet.EventResult type is needed which is in charge to report back if the .NET side implemented the method and, in case of an available result, contains the result to be returned:

class EventResult {
   boolean _hasImplementation = false;
   Object _returnData;

   public boolean getHasImplementation() {
      return _hasImplementation;
   }

   public boolean setHasImplementation(boolean hasImplementation) {
      _hasImplementation = hasImplementation;
   }

   public Object getReturnData() {
      return _returnData;
   }

   public void setReturnData(Object retData) {
      _returnData = retData;
   }
}

To obtain the previous extended behavior maybe a new class shall be created which extends MASES.JCOBridge.C2JBridge.JVMBridgeListener and implements the needed methods.

@masesdevelopers
Copy link
Contributor Author

Maybe the changes requested from #512 (comment) shall be managed differentiating the repository between:

  1. the developed side associated to JNetReflector (e.g. the previous and new classes needed) and core of JNet
  2. the classes generated using JNetReflector and classes extending the generated one

Point 1 can be associated to #513 since many classes are used from Netdroid or other projects based on JNetReflector

@masesdevelopers
Copy link
Contributor Author

Reopen since #525 partially covers the needs of this issue

@masesdevelopers
Copy link
Contributor Author

Latest step to cover the request of masesgroup/netdroid#75 is to add a sort of method filter using a new configuration property:

  • the property lists the classes shall be filtered
  • a sub property declares, using patterns, the methods shall be managed

If a class does not belong to the new property, the tool will use a the default pattern * that means everything like it is done now.

masesdevelopers added a commit to masesdevelopers/JNet that referenced this issue Sep 19, 2024
…hCallbacks to manage classes which defines methods and optionally declares some methods to be managed as callbacks
masesdevelopers added a commit that referenced this issue Sep 19, 2024
… to manage classes which defines methods and optionally declares some methods to be managed as callbacks (#528)
@masesdevelopers
Copy link
Contributor Author

Looking at the code in masesgroup/netdroid#101 generated using latest JNetReflector a missing functionality is not available. When a method is overridden the base method sometime shall be invoked like in the example reported in masesgroup/netdroid#75 (comment).
The code:

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState, PersistableBundle pers) {
        super.onCreate(savedInstanceState, pers);
        setContentView(R.layout.activity_main);
    }
}

needs to invoke super.onCreate(savedInstanceState, pers);, however it is invoked only if .NET does not override the C# method:

    //@Override
    public void onCreate(android.os.Bundle arg0, android.os.PersistableBundle arg1) {
        org.mases.jnet.developed.JNetEventResult eventDataExchange = new org.mases.jnet.developed.JNetEventResult();
        raiseEvent("onCreate", eventDataExchange, arg0, arg1); if (!eventDataExchange.getHasOverride()) super.onCreate(arg0, arg1);
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment