How to visit method invocation in newest version of Android Lint - java

I am writing a new Android lint rule which need to visit all method invocations and then it analyzes annotation of each method to decide handling. However, I am getting trouble which very first step: visit all method invocations.
In conclusion, all I want is that when I have the code below, the 2 final lines (test.testMethod(); and testMethod();) are highlighted as error by Android Studio.
public class MyClass {
class TestClass {
void testMethod() {}
}
void testMethod() {}
void testCall() {
TestClass test = new TestClass();
test.testMethod(); // expect highlight
testMethod(); // expect highlight
}
}
In the old version of lint framework I think I can achieve this by:
public class MyDetector extends Detector implements Detector.JavaScanner {
#Override
public
List<Class<? extends Node>> getApplicableNodeTypes() {
//noinspection unchecked
return Arrays.<Class<? extends Node>>asList(MethodInvocation.class);
}
#Nullable
#Override
public AstVisitor createJavaVisitor(#NonNull JavaContext context) {
return new CallVisitor(context);
}
private class CallVisitor extends ForwardingAstVisitor {
private final JavaContext mContext;
public CallVisitor(JavaContext context) {
mContext = context;
}
#Override
public boolean visitMethodInvocation(#NonNull MethodInvocation call) {
String message = "Nguyen Ha Quang";
mContext.report(THREAD, call, mContext.getLocation(call), message);
return false;
}
}
}
This code is referenced from https://cs.android.com/android/platform/superproject/+/master:tools/base/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SupportAnnotationDetector.java;bpv=0 (line 1516 -> end).
Unfortunately, since AndroidX was released, this code is no longer work. Now, I must try to do something like this:
public class ChatWorkerDetector extends Detector implements Detector.UastScanner {
#Nullable
#Override
public List<Class<? extends UElement>> getApplicableUastTypes() {
return Arrays.<Class<? extends UElement>>asList(
UCallableReferenceExpression.class
);
}
#Override
public UElementHandler createUastHandler(JavaContext context) {
return new CustomElementHandler(context);
}
public class CustomElementHandler extends UElementHandler {
JavaContext mContext;
public CustomElementHandler(JavaContext mContext) {
this.mContext = mContext;
}
#Override
public void visitCallableReferenceExpression(UCallableReferenceExpression node) {
String message = "Nguyen Ha Quang";
mContext.report(THREAD, node, mContext.getLocation(node), message);
}
}
}
This code still does not work as I expect and I also have tried: visitCallableReferenceExpression, visitMethod, visitCallExpression, but none of them work.
Is any visitABC function available in UElementHandler class which can do my job?
UElementHandler source code: https://github.com/ouyangpeng/android-lint-checks-studio3.0/blob/master/libs/lint-api/src/main/java/com/android/tools/lint/client/api/UElementHandler.java
Thank you very much.

Related

What is the functional difference between these two definitions?

// in PingPongMessage.java
public class PingPong {
public static final class Ping { }
}
// in PingActor.java
public class PingActor extends AbstractBehavior<PingPong.Ping> {
public static Behavior<PingPong.Ping> create() {
return Behaviors.setup(context -> new PingActor(context));
}
private PingActor(ActorContext<PingPong.Ping> context){
super(context);
}
#Override
public Receive<PingPong.Ping> createReceive() {
return newReceiveBuilder().onMessage(PingPong.Ping.class, this::onPingMsg).build();
}
private Behavior<PingPong.Ping> onPingMsg() {
System.out.println("Ping!");
return this;
}
}
vs.
// in PingActor.java
public class PingActor extends AbstractBehavior<PingActor.Ping>{
public static final class Ping {
}
public static Behavior<Ping> create() {
return Behaviors.setup(context -> new PingActor(context));
}
private PingActor(ActorContext<Ping> context){
super(context);
}
#Override
public Receive<Ping> createReceive() {
return newReceiveBuilder()
.onMessage(Ping.class, this::onPingMessage).build();
}
private Behavior<Ping> onPingMessage(Ping message){
System.out.println("Ping!");
return this;
}
}
I am trying to understand the relationship between Ping and PingActor in both the cases. In my opinion both are doing the same thing, but in one case PingPong.Ping is defined inside PingActor and in the other case PingPong is another class outside of it. Obviously they are not the same because the second example seems to compile, but the first does not.
The compiler error is -
Inferred type 'M' for type parameter 'M' is not within its bound; should extend 'com.lightbend.akka.sample.PingPong.Ping seen in Receive<PingPong.Ping> createReceive()
You cannot have a public class "PingPong" inside PingPongMessage.java
They don't match

How to check typed callback type inside Fragment.onAttach()

I'm trying to implement abstract fragment with typed callback to use it in several subclasses.
How can I check if Context is instance of appropriate class?
My code of abstact CallbackFragment:
public abstract class CallbackFragment<C> extends Fragment {
protected C mCallback;
public CallbackFragment() {
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
//just in case
if(context == null)
throw new NullPointerException();
try {
mCallback = (C) context; //this line not seems to throw any exception
} catch (ClassCastException exception) {
throw new RuntimeException(context.toString() + " must implement Callbacks");
}
}
#Override
public void onDetach() {
super.onDetach();
mCallback = null;
}
}
Vehicle list fragment:
public abstract class VehicleListFragment<T extends Vehicle>
extends CallbackFragment<VehicleListFragment.Callback<T>> {
//callback for any list of any vehicle
public interface Callback<T extends Vehicle> {
void onListItemSelected(T selectedItem);
}
//common code for list of any vehicle
public VehicleListFragment() {
}
}
Bus, Truck, Boat, Bike, whatever list fragment:
public class BusListFragment
extends VehicleListFragment<Bus> {
//code specific for list of bus
public BusListFragment() {
}
}
Vehicle details fragment:
public abstract class VehicleDetailsFragment<T extends Vehicle, C extends VehicleDetailsFragment.Callback<T>>
extends CallbackFragment<C> {
//common methods of callback for any vehicle
public interface Callback<T> {
void onVehicleEdited(T editeItem);
}
//common code for any vehicle
public VehicleDetailsFragment() {
}
}
Bus, Truck, Boat, Bike, whatever details fragment:
public class BusDetailsFragment
extends VehicleDetailsFragment<Bus, BusDetailsFragment.Callback> {
//specific for Bus methods
public interface Callback
extends VehicleDetailsFragment.Callback<Bus> {
void onSomethingSpecificForBusHappened(Bus bus);
}
//code specific for Bus
public BusDetailsFragment() {
}
}
I've tried to add an abstract method for CallbackFragment to get callback class:
public abstract class CallbackFragment<C> extends Fragment {
...
#NonNull
protected abstract Class<C> getCallbackClass();
#Override
public void onAttach(Context context) {
super.onAttach(context);
...
//now checking instanceof like this
if(!getCallbackClass().isAssignableFrom(context.getClass())){
throw new RuntimeException(context.toString() + " must implement Callbacks");
}
}
}
With BusDetailsFragment everything looks OK:
public class BusDetailsFragment
extends VehicleDetailsFragment<Bus, BusDetailsFragment.Callback> {
#NonNull
#Override
protected Class<Callback> getCallbackClass() {
return Callback.class;
}
...
}
But not with BusListFragment:
public class BusListFragment
extends VehicleListFragment<Bus> {
#NonNull
#Override
protected Class<Callback<Bus>> getCallbackClass() {
/**
* I'm not seeing any option here
*
* mCallback - is null yet. So, there is no way to use mCallback.getClass()
*
* Callback<Bus>.class - Cannot select from parameterized type
*/
//return mCallback.getClass();
//return Callback<Bus>.class;
}
...
}
Of course, I could create an own interface for every subclass of VehicleListFragment that extends VehicleListFragment.Callback (like in subclasses of VehicleDetailsFragment) but it will always look like this:
public interface Callback
extends VehicleListFragment.Callback<Bus> {
//nothing more here
}
This doesn't look like the best option for me. Maybe there is any other solution? Please share your thoughts. ANY help would be appreciated.
mCallback = (C) context; //this line not seems to throw any exception
this call will never throw an Exception. During Runtime, your C is replaced with Object(that's called Type-Erasure) - and everything is an Object. Therefore you can assign anything at this point.
To have the exception (or at least error-determination) at the point, where you need it, you can use:
public abstract class CallbackFragment<C> extends Fragment {
protected C mCallback;
protected Class<C> callbackClass;
public CallbackFragment(Class<C> clazz) {
this.callbackClass = clazz;
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
//just in case
if(context == null)
throw new NullPointerException();
if (clazz.isAssignableFrom(context.getClass()){
mCallback = (C) context;
}else{
//oops
}
}
}
ofc. then your FragmentCreation would change from
CallbackFragment<Something> fragment = new CallbackFragment<Something>();
to
CallbackFragment<Something> fragment = new CallbackFragment<Something>(Something.class);
It's a little different, but allows you to keep track of the actual type at any time, bypassing the Type-Erasure.
ps.: For Inherited classes, you can do it more generic:
public abstract class CallbackFragment<C> extends Fragment {
protected Class<C> callbackClass;
public CallbackFragment() {
this.callbackClass = (Class<C>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];;
}
}
public class CallbackFragmentOfSomething extends <CallbackFragment<Something>>{
}
This only fails, if your actual class is not defined due to inheritance, but "on the fly":
CallbackFragment<Something> fragment = new CallbackFragment<Something>();
(Everything untested / no copy paste, but should be somewhat accurate)

How to correctly shut down a JUnit-Runner?

I made my own JUnit-Runner by implementing org.junit.runner.Runner, so that I can run my UnitTests with them using the #RunWith-Annotation.
It lookes somewhat like this:
public class MyRunner extends Runner {
private Context myContext;
myContext.init();
private final BlockJUnit4ClassRunner runner;
public MyRunner(final Class<?> clazz) throws InitializationError {
myContext = new Context();
runner = new BlockJUnit4ClassRunner(clazz);
}
#Override
public void run(final RunNotifier notifier) {
runner.run(notifier);
}
#Override
public Description getDescription() {
return runner.getDescription();
}
public void filter(final Filter filter) throws NoTestsRemainException {
runner.filter(filter);
}
}
To clean up resources, I have to shut down MyContext by calling MyContext.close(). Where should I invoke this so that my resources are cleand up after the tests have run?
I'm not sure what you're trying to achive but have you already had a look at JUnit's Rules?
public class MyContextRule extends ExternalResource {
private final Context myContext;
public MyContextRule() {
myContext = new Context();
}
#Override
protected void before() throws Throwable {
myContext.init();
}
#Override
protected void after() {
myContext.close();
}
}
Usage:
public class MyTest {
#ClassRule
public static MyContextRule contextRule = new MyContextRule();
//...
}
JUnit Rules advantage over Runners is that you can have multiple of them, while you only can have one runner.
So, your custom Rule could be used with any runner that may be introduced by a random testframework that you may come across in the future...
Where should I invoke this so that my resources are cleand up after
the tests have run ?
UPDATED MY ANSWER, you can use org.junit.runner.notification.RunListener as shown below:
(1) Create your own RunListener class:
public class MyRunnerListener extends RunListener {
private Context context;
public MyRunnerListener(Context context) {
this.context = context;
}
void testRunFinished(Result result) {
context.close();
}
}
(2) Use the MyRunnerListener inside MyRunner :
public class MyRunner extends Runner {
private Context myContext;
MyRunnerListener runnerListener;
private final BlockJUnit4ClassRunner runner;
public MyRunner(final Class<?> clazz) throws InitializationError {
myContext = new Context();
myContext.init();
runnerListener = new MyRunnerListener(myContext);
runner = new BlockJUnit4ClassRunner(clazz);
}
#Override
public void run(final RunNotifier notifier) {
notifier.addListener(runnerListener);
runner.run(notifier);
}
#Override
public Description getDescription() {
return runner.getDescription();
}
public void filter(final Filter filter) throws NoTestsRemainException {
runner.filter(filter);
}
}
P.S.: If you don't want to use the Runner, then you can follow the answer from Markus (which uses TestRule, NOT TestRunner).

How to properly convert Listeners to Reactive (Observables) using RxJava?

I'm using a multiplayer Game Client that's called AppWarp (http://appwarp.shephertz.com), where you can add event listeners to be called back when event's happen, let's assume we'll be talking about the Connection Listener, where you need to implement this interface:
public interface ConnectionRequestListener {
void onConnectDone(ConnectEvent var1);
void onDisconnectDone(ConnectEvent var1);
void onInitUDPDone(byte var1);
}
My goal here is to mainly create a Reactive version of this client to be used in my Apps Internally instead of using the Client itself directly (I'll also rely on interfaces later instead of just depending on the WarpClient itself as in the example, but that's not the important point, please read my question at the very end).
So what I did is as follows:
1) I introduced a new event, named it RxConnectionEvent (Which mainly groups Connection-Related events) as follows:
public class RxConnectionEvent {
// This is the original connection event from the source client
private final ConnectEvent connectEvent;
// this is to identify if it was Connection / Disconnection
private final int eventType;
public RxConnectionEvent(ConnectEvent connectEvent, int eventType) {
this.connectEvent = connectEvent;
this.eventType = eventType;
}
public ConnectEvent getConnectEvent() {
return connectEvent;
}
public int getEventType() {
return eventType;
}
}
2) Created some event types as follows:
public class RxEventType {
// Connection Events
public final static int CONNECTION_CONNECTED = 20;
public final static int CONNECTION_DISCONNECTED = 30;
}
3) Created the following observable which emits my new RxConnectionEvent
import com.shephertz.app42.gaming.multiplayer.client.WarpClient;
import com.shephertz.app42.gaming.multiplayer.client.events.ConnectEvent;
import rx.Observable;
import rx.Subscriber;
import rx.functions.Action0;
import rx.subscriptions.Subscriptions;
public class ConnectionObservable extends BaseObservable<RxConnectionEvent> {
private ConnectionRequestListener connectionListener;
// This is going to be called from my ReactiveWarpClient (Factory) Later.
public static Observable<RxConnectionEvent> createConnectionListener(WarpClient warpClient) {
return Observable.create(new ConnectionObservable(warpClient));
}
private ConnectionObservable(WarpClient warpClient) {
super(warpClient);
}
#Override
public void call(final Subscriber<? super RxConnectionEvent> subscriber) {
subscriber.onStart();
connectionListener = new ConnectionRequestListener() {
#Override
public void onConnectDone(ConnectEvent connectEvent) {
super.onConnectDone(connectEvent);
callback(new RxConnectionEvent(connectEvent, RxEventType.CONNECTION_CONNECTED));
}
#Override
public void onDisconnectDone(ConnectEvent connectEvent) {
super.onDisconnectDone(connectEvent);
callback(new RxConnectionEvent(connectEvent, RxEventType.CONNECTION_DISCONNECTED));
}
// not interested in this method (for now)
#Override
public void onInitUDPDone(byte var1) { }
private void callback(RxConnectionEvent rxConnectionEvent)
{
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(rxConnectionEvent);
} else {
warpClient.removeConnectionRequestListener(connectionListener);
}
}
};
warpClient.addConnectionRequestListener(connectionListener);
subscriber.add(Subscriptions.create(new Action0() {
#Override
public void call() {
onUnsubscribed(warpClient);
}
}));
}
#Override
protected void onUnsubscribed(WarpClient warpClient) {
warpClient.removeConnectionRequestListener(connectionListener);
}
}
4) and finally my BaseObservable looks like the following:
public abstract class BaseObservable<T> implements Observable.OnSubscribe<T> {
protected WarpClient warpClient;
protected BaseObservable (WarpClient warpClient)
{
this.warpClient = warpClient;
}
#Override
public abstract void call(Subscriber<? super T> subscriber);
protected abstract void onUnsubscribed(WarpClient warpClient);
}
My question is mainly: is my implementation above correct or should I instead create separate observable for each event, but if so, this client has more than 40-50 events do I have to create separate observable for each event?
I also use the code above as follows (used it in a simple "non-final" integration test):
public void testConnectDisconnect() {
connectionSubscription = reactiveWarpClient.createOnConnectObservable(client)
.subscribe(new Action1<RxConnectionEvent>() {
#Override
public void call(RxConnectionEvent rxEvent) {
assertEquals(WarpResponseResultCode.SUCCESS, rxEvent.getConnectEvent().getResult());
if (rxEvent.getEventType() == RxEventType.CONNECTION_CONNECTED) {
connectionStatus = connectionStatus | 0b0001;
client.disconnect();
} else {
connectionStatus = connectionStatus | 0b0010;
connectionSubscription.unsubscribe();
haltExecution = true;
}
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
fail("Unexpected error: " + throwable.getMessage());
haltExecution = true;
}
});
client.connectWithUserName("test user");
waitForSomeTime();
assertEquals(0b0011, connectionStatus);
assertEquals(true, connectionSubscription.isUnsubscribed());
}
I suggest you avoid extending the BaseObservable directly since it's very error prone. Instead, try using the tools Rx itself gives you to create your observable.
The easiest solution is using a PublishSubject, which is both an Observable and a Subscriber. The listener simply needs to invoke the subject's onNext, and the subject will emit the event. Here's a simplified working example:
public class PublishSubjectWarpperDemo {
public interface ConnectionRequestListener {
void onConnectDone();
void onDisconnectDone();
void onInitUDPDone();
}
public static class RxConnectionEvent {
private int type;
public RxConnectionEvent(int type) {
this.type = type;
}
public int getType() {
return type;
}
public String toString() {
return "Event of Type " + type;
}
}
public static class SimpleCallbackWrapper {
private final PublishSubject<RxConnectionEvent> subject = PublishSubject.create();
public ConnectionRequestListener getListener() {
return new ConnectionRequestListener() {
#Override
public void onConnectDone() {
subject.onNext(new RxConnectionEvent(1));
}
#Override
public void onDisconnectDone() {
subject.onNext(new RxConnectionEvent(2));
}
#Override
public void onInitUDPDone() {
subject.onNext(new RxConnectionEvent(3));
}
};
}
public Observable<RxConnectionEvent> getObservable() {
return subject;
}
}
public static void main(String[] args) throws IOException {
SimpleCallbackWrapper myWrapper = new SimpleCallbackWrapper();
ConnectionRequestListener listner = myWrapper.getListener();// Get the listener and attach it to the game here.
myWrapper.getObservable().observeOn(Schedulers.newThread()).subscribe(event -> System.out.println(event));
listner.onConnectDone(); // Call the listener a few times, the observable should print the event
listner.onDisconnectDone();
listner.onInitUDPDone();
System.in.read(); // Wait for enter
}
}
A more complex solution would be to use one of the onSubscribe implementations to create an observable using Observable.create(). For example AsyncOnSubscibe. This solution has the benefit of handling backperssure properly, so your event subscriber doesn't become overwhelmed with events. But in your case, that sounds like an unlikely scenario, so the added complexity is probably not worth it.

Virtual behavior with java's inheritance with Generic parent

I have a "Data manager" class, which contains a list of some sort of object type
Then, when I started getting my code advanced, I had the need for a different kind of manager, which has some similarities - loadData(..), saveData(..), add(..), remove(..)
but also some differences, which is unique to each kind of manager, depending on the type of which it is "managing".
So I decided to do the following structure:
Generic class with singelton implementation for each child (At first, my manager was static as it has no sense of being multiple instances):
public abstract class GenericDataManager<T> {
protected List<T> list;
protected void saveData(Context context, String filePath) {..}
protected void loadData(Context context, String filePath) {..}
..
}
and my two managers are the following:
public class ManagerA extends GenericDataManager<A> {
private static ManagerA instance = null;
protected ManagerA() { }
public static ManagerA getInstance() {
if (instance == null)
instance = new ManagerA();
return instance;
}
private void saveData(Context context) {
saveData(context, "fileA");
}
private void loadData(Context context) {
loadData(context, "fileA");
}
}
public class ManagerB extends GenericDataManager<B> {
private static ManagerB instance = null;
protected ManagerB() { }
public static ManagerB getInstance() {
if (instance == null)
instance = new ManagerB();
return instance;
}
private void saveData(Context context) {
saveData(context, "fileB");
}
private void loadData(Context context) {
loadData(context, "fileB");
}
}
I showed here only the similar pieces of code for the two managers, and you can already see the problem - Although I managed to make my similar piece of code implemented only one and reused for every specific implementation using generics and inheritance mechanism, I still need the only specific information, which is the data file to use.
Is there a way of the generic parent to request that information from its child, so that I wont need the redundant implementation in the child class?
Leaving it this way makes me feel Im missing something.
How about:
public abstract class GenericDataManager<T> {
protected abstract String getFilePath();
protected List<T> list;
protected void saveData(Context context) {
String filePath = getFilePath();
..
}
protected void loadData(Context context) {
String filePath = getFilePath();
..
}
..
}
and then:
public class ManagerA extends GenericDataManager<A> {
private static ManagerA instance = null;
protected ManagerA() { }
public static ManagerA getInstance() {
if (instance == null)
instance = new ManagerA();
return instance;
}
protected String getFilePath() { return "fileA" );
}
public class ManagerB extends GenericDataManager<A> {
private static ManagerB instance = null;
protected ManagerB() { }
public static ManagerB getInstance() {
if (instance == null)
instance = new ManagerB();
return instance;
}
protected String getFilePath() { return "fileB" );
}
You could have the parent call back to the children, but it's generally poor practice. Inheritance always requires tightly coupling the children to the parent, but you generally want to avoid coupling the parent to the children.
Instead, perhaps in GenericDataManager:
protected void saveData(Context context, string fileName) {
// do the generic work with the specific given file
}
protected void loadData(Context context) {
// do the generic work with the specific given file
}
...and then in the subclasses:
private void loadData(Context context) {
super.loadData(context, "loadA"); // or of course, "loadB"
}
Yes. Make an abstract getter in the parent class, and add implementation in children:
abstract class GenericDataManager<T> {
protected void saveData(Context context) {
String filePath = getFilePath();
}
protected void loadData(Context context) {
String filePath = getFilePath();
}
protected abstract String getFilePath();
}
public class ManagerA extends GenericDataManager<A> {
#Override protected String getFilePath() {
return "fileA";
}
}

Categories

Resources