1 /*
2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.sqlite.inspection;
18 
19 import android.annotation.SuppressLint;
20 import android.util.Log;
21 
22 import androidx.inspection.ArtTooling;
23 
24 import org.jspecify.annotations.NonNull;
25 
26 import java.lang.reflect.InvocationTargetException;
27 import java.lang.reflect.Method;
28 import java.util.Objects;
29 
30 class SqlDelightInvalidation implements Invalidation {
31     public static final String TAG = "StudioInspectors";
32     public static final String HIDDEN_TAG = "studio.inspectors";
33 
34     private static final String SQLDELIGHT_QUERY_CLASS_NAME = "com.squareup.sqldelight.Query";
35     private static final String SQLDELIGHT_NOTIFY_METHOD_NAME = "notifyDataChanged";
36 
37     private final @NonNull ArtTooling mArtTooling;
38     private final @NonNull Class<?> mQueryClass;
39     private final @NonNull Method mNotifyDataChangeMethod;
40 
create(@onNull ArtTooling artTooling)41     static @NonNull Invalidation create(@NonNull ArtTooling artTooling) {
42         ClassLoader classLoader = SqlDelightInvalidation.class.getClassLoader();
43         Objects.requireNonNull(classLoader);
44         try {
45             Class<?> queryClass = classLoader.loadClass(SQLDELIGHT_QUERY_CLASS_NAME);
46             Method notifyMethod = queryClass.getMethod(SQLDELIGHT_NOTIFY_METHOD_NAME);
47             return new SqlDelightInvalidation(artTooling, queryClass, notifyMethod);
48         } catch (ClassNotFoundException e) {
49             Log.v(HIDDEN_TAG, "SqlDelight not found", e);
50             return () -> {
51             };
52         } catch (Exception e) {
53             Log.w(TAG, "Error setting up SqlDelight invalidation", e);
54             return () -> {
55             };
56         }
57     }
58 
SqlDelightInvalidation(@onNull ArtTooling artTooling, @NonNull Class<?> queryClass, @NonNull Method notifyDataChangeMethod)59     private SqlDelightInvalidation(@NonNull ArtTooling artTooling, @NonNull Class<?> queryClass,
60             @NonNull Method notifyDataChangeMethod) {
61         mArtTooling = artTooling;
62         mQueryClass = queryClass;
63         mNotifyDataChangeMethod = notifyDataChangeMethod;
64     }
65 
66     @SuppressLint("BanUncheckedReflection")
67     @Override
triggerInvalidations()68     public void triggerInvalidations() {
69         // invalidating all queries because we can't say which ones were actually affected.
70         for (Object query: mArtTooling.findInstances(mQueryClass)) {
71             try {
72                 mNotifyDataChangeMethod.invoke(query);
73             } catch (IllegalAccessException | InvocationTargetException e) {
74                 Log.w(TAG, "Error calling notifyDataChanged", e);
75             }
76         }
77     }
78 }
79