{
261 | _data = data
262 | _code = code
263 | _message = errorMsg
264 | return super.notifyData()
265 | }
266 | }
267 | ```
268 |
269 | > 如果您有任何建议或者问题请联系,可直接联系我
270 |
--------------------------------------------------------------------------------
/net/src/main/java/com/lvhttp/net/utils/FileQUtils.java:
--------------------------------------------------------------------------------
1 | package com.lvhttp.net.utils;
2 |
3 | import android.content.ContentResolver;
4 | import android.content.ContentValues;
5 | import android.content.Context;
6 | import android.content.res.AssetFileDescriptor;
7 | import android.database.Cursor;
8 | import android.graphics.Bitmap;
9 | import android.graphics.BitmapFactory;
10 | import android.net.Uri;
11 | import android.os.Build;
12 | import android.os.Bundle;
13 | import android.os.Environment;
14 | import android.provider.MediaStore;
15 | import android.text.TextUtils;
16 | import android.util.Log;
17 |
18 | import androidx.annotation.RequiresApi;
19 |
20 | import java.io.BufferedInputStream;
21 | import java.io.File;
22 | import java.io.FileInputStream;
23 | import java.io.FileNotFoundException;
24 | import java.io.IOException;
25 | import java.io.InputStream;
26 | import java.io.OutputStream;
27 | import java.util.Objects;
28 |
29 | /**
30 | * Created by Petterp
31 | * on 2019-12-27
32 | *
33 | * 以下方法适用于Android10
34 | *
35 | * Android10 文件存储机制为沙盒存储,每个App都只能访问自身目录下的文件(私有文件)和公共存储文件
36 | *
37 | * 什么是私有文件?
38 | * 包名底下的文件,如 com.business.toos/xxx,读写私有文件无需任何权限
39 | *
40 | * 什么是公有文件?
41 | * 如DCIM,PICTURES,MUSIC,Movies...,访问共有文件还是需要申请权限
42 | *
43 | * 在AndroidQ中,文件的操作使用 ContentResolver 进行操作
44 | * 获取及创建私有文件使用 context.getExternalFilesDir( xxx )
45 | * 需要注意,沙盒文件会在卸载App之后被删除
46 | *
47 | * 公共目录下文件操作
48 | * 通过MediaStore
49 | *
50 | * 官网链接 https://developer.android.google.cn/training/data-storage/files/media
51 | */
52 | public class FileQUtils {
53 |
54 | private static final String TAG = "FileQUtils";
55 |
56 | /**
57 | * 用于Android10保存image
58 | *
59 | * @param context
60 | * @param saveFileName 文件名
61 | * @param saveDirName 子文件路径,如果不存在则新建
62 | * @return
63 | */
64 | @RequiresApi(api = Build.VERSION_CODES.Q)
65 | public static Uri saveImageWithAndroidQ(Context context,
66 | String saveFileName,
67 | String saveDirName) {
68 | Uri uri = null;
69 | ContentValues values = new ContentValues();
70 | values.put(MediaStore.Images.Media.DISPLAY_NAME, saveFileName);
71 | values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
72 | values.put(MediaStore.Images.Media.RELATIVE_PATH, "DCIM/" + saveDirName);
73 | Uri external = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
74 | ContentResolver resolver = context.getContentResolver();
75 |
76 | //这里为了避免用户如果删除了DCIM文件夹,此时让系统帮我们去寻找合适的存储路径
77 | //适合Android10
78 | try {
79 | uri = resolver.insert(external, values);
80 | } catch (IllegalArgumentException e) {
81 | values.remove(MediaStore.Images.Media.RELATIVE_PATH);
82 | uri = resolver.insert(external, values);
83 | } finally {
84 | return uri;
85 | }
86 | }
87 |
88 | /**
89 | * 用户保存任何文件时传入
90 | *
91 | * @param context
92 | * @param saveFileName 文件名
93 | * @param mineType 文件类型,参考FileIntentUtils类中的getMap
94 | * @param saveDirName 文件路径
95 | * @return
96 | */
97 | @RequiresApi(api = Build.VERSION_CODES.Q)
98 | public static Uri saveFileWithAndroidQ(Context context,
99 | String saveFileName,
100 | String mineType,
101 | String saveDirName) {
102 | Uri uri = null;
103 | ContentValues values = new ContentValues();
104 | values.put(MediaStore.Images.Media.DISPLAY_NAME, saveFileName);
105 | values.put(MediaStore.Images.Media.MIME_TYPE, mineType);
106 | values.put(MediaStore.Images.Media.RELATIVE_PATH, "Download/" + saveDirName);
107 | Uri external = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
108 | ContentResolver resolver = context.getContentResolver();
109 |
110 | //这里为了避免用户如果删除了Download文件夹,此时让系统帮我们去寻找合适的存储路径
111 | //适合Android10
112 | try {
113 | uri = resolver.insert(external, values);
114 | } catch (IllegalArgumentException e) {
115 | values.remove(MediaStore.Images.Media.RELATIVE_PATH);
116 | uri = resolver.insert(external, values);
117 | } finally {
118 | return uri;
119 | }
120 | }
121 |
122 | /**
123 | * 用于将Uri转为File
124 | *
125 | * @param uri
126 | * @param context
127 | * @return
128 | */
129 | @RequiresApi(api = Build.VERSION_CODES.KITKAT)
130 | public static File getFileByUri(Uri uri, Context context) {
131 | String path = null;
132 | if ("file".equals(uri.getScheme())) {
133 | path = uri.getEncodedPath();
134 | if (path != null) {
135 | path = Uri.decode(path);
136 | ContentResolver cr = context.getContentResolver();
137 | Cursor cur = cr.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[]{MediaStore.Images.ImageColumns._ID, MediaStore.Images.ImageColumns.DATA}, "(" + MediaStore.Images.ImageColumns.DATA + "=" + "'" + path + "'" + ")", null, null);
138 | int index = 0;
139 | int dataIdx = 0;
140 | for (Objects.requireNonNull(cur).moveToFirst(); !cur.isAfterLast(); cur.moveToNext()) {
141 | index = cur.getColumnIndex(MediaStore.Images.ImageColumns._ID);
142 | index = cur.getInt(index);
143 | dataIdx = cur.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
144 | path = cur.getString(dataIdx);
145 | }
146 | cur.close();
147 | if (index != 0) {
148 | Uri u = Uri.parse("content://media/external/images/media/" + index);
149 | System.out.println("temp uri is :" + u);
150 | }
151 | }
152 | if (path != null) {
153 | return new File(path);
154 | }
155 | } else if ("content".equals(uri.getScheme())) {
156 | // 4.2.2以后
157 | String[] proj = {MediaStore.Images.Media.DATA};
158 | Cursor cursor = context.getContentResolver().query(uri, proj, null, null, null);
159 | if (Objects.requireNonNull(cursor).moveToFirst()) {
160 | int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
161 | path = cursor.getString(columnIndex);
162 | }
163 | cursor.close();
164 | return new File(Objects.requireNonNull(path));
165 | }
166 | return null;
167 | }
168 |
169 |
170 | /**
171 | * 判断共有文件是否存在
172 | *
173 | * @param context
174 | * @param path
175 | * @return
176 | */
177 | public static boolean isAndroidQFileExists(Context context, String path) {
178 | AssetFileDescriptor afd;
179 | ContentResolver cr = context.getContentResolver();
180 | try {
181 | Uri uri = Uri.parse(path);
182 | //r表示读文件 w表示写
183 | afd = cr.openAssetFileDescriptor(uri, "r");
184 | if (afd == null) {
185 | return false;
186 | }
187 | } catch (FileNotFoundException e) {
188 | return false;
189 | }
190 | return true;
191 | }
192 |
193 | /**
194 | * 图片保存
195 | *
196 | * @param context context
197 | * @param sourceFile 源文件
198 | * @param saveFileName 保存的文件名
199 | * @param saveDirName picture子目录
200 | * @return 成功或者失败
201 | */
202 | @RequiresApi(api = Build.VERSION_CODES.Q)
203 | public static boolean saveImageWithAndroidQ(Context context,
204 | File sourceFile,
205 | String saveFileName,
206 | String saveDirName) {
207 | if (!isExternalStorageReadable()) {
208 | throw new RuntimeException("External storage cannot be written!");
209 | }
210 | ContentValues values = new ContentValues();
211 | values.put(MediaStore.Images.Media.DISPLAY_NAME, saveFileName);
212 | values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
213 | values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/" + saveDirName);
214 |
215 | Uri external = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
216 | ContentResolver resolver = context.getContentResolver();
217 |
218 | Uri insertUri = resolver.insert(external, values);
219 | BufferedInputStream inputStream;
220 | OutputStream os = null;
221 | boolean result;
222 | try {
223 | inputStream = new BufferedInputStream(new FileInputStream(sourceFile));
224 | if (insertUri != null) {
225 | os = resolver.openOutputStream(insertUri);
226 | }
227 | if (os != null) {
228 | byte[] buffer = new byte[1024 * 4];
229 | int len;
230 | while ((len = inputStream.read(buffer)) != -1) {
231 | os.write(buffer, 0, len);
232 | }
233 | os.flush();
234 | }
235 | result = true;
236 | } catch (IOException e) {
237 | result = false;
238 | }
239 | return result;
240 | }
241 |
242 |
243 | /**
244 | * 复制或下载文件到公有目录
245 | *
246 | * @param context
247 | * @param sourcePath
248 | * @param mimeType
249 | * @param fileName
250 | * @param saveDirName
251 | * @return
252 | */
253 | @RequiresApi(api = Build.VERSION_CODES.Q)
254 | public static Uri copyToDownloadAndroidQ(Context context, String sourcePath, String mimeType, String fileName, String saveDirName) {
255 | if (!isExternalStorageReadable()) {
256 | throw new RuntimeException("External storage cannot be written!");
257 | }
258 | ContentValues values = new ContentValues();
259 | //显示名称
260 | values.put(MediaStore.Downloads.DISPLAY_NAME, fileName);
261 | //存储文件的类型
262 | values.put(MediaStore.Downloads.MIME_TYPE, mimeType);
263 | //公有文件路径
264 | values.put(MediaStore.Downloads.RELATIVE_PATH, "Download/" + saveDirName.replaceAll("/", "") + "/");
265 |
266 | //生成一个Uri
267 | Uri external = MediaStore.Downloads.EXTERNAL_CONTENT_URI;
268 | ContentResolver resolver = context.getContentResolver();
269 |
270 | //写入
271 | Uri insertUri = resolver.insert(external, values);
272 | if (insertUri == null) {
273 | return null;
274 | }
275 |
276 | String mFilePath = insertUri.toString();
277 |
278 | InputStream is = null;
279 | OutputStream os = null;
280 | try {
281 | //输出流
282 | os = resolver.openOutputStream(insertUri);
283 | if (os == null) {
284 | return null;
285 | }
286 | int read;
287 | File sourceFile = new File(sourcePath);
288 | if (sourceFile.exists()) { // 文件存在时
289 | is = new FileInputStream(sourceFile); // 读入原文件
290 | byte[] buffer = new byte[1444];
291 | while ((read = is.read(buffer)) != -1) {
292 | //写入uri中
293 | os.write(buffer, 0, read);
294 | }
295 | }
296 | } catch (Exception e) {
297 | e.printStackTrace();
298 | }
299 | return insertUri;
300 |
301 | }
302 |
303 |
304 | /**
305 | * 判断外部存储是否可写入读取
306 | *
307 | * @return
308 | */
309 | public boolean isExternalStorageWritable() {
310 | String state = Environment.getExternalStorageState();
311 | if (Environment.MEDIA_MOUNTED.equals(state)) {
312 | return true;
313 | }
314 | return false;
315 | }
316 |
317 |
318 | /**
319 | * 判断外部存储是否可写入
320 | *
321 | * @return
322 | */
323 | public static boolean isExternalStorageReadable() {
324 | String state = Environment.getExternalStorageState();
325 | if (Environment.MEDIA_MOUNTED.equals(state) ||
326 | Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
327 | return true;
328 | }
329 | return false;
330 | }
331 |
332 |
333 | /**
334 | * 获取私有相册目录
335 | * 私有目录会被删除
336 | *
337 | * @param context
338 | * @param albumName
339 | * @return
340 | */
341 | public File getPrivateAlbumStorageDir(Context context, String albumName) {
342 | // Get the directory for the app's private pictures directory.
343 | File file = new File(context.getExternalFilesDir(
344 | Environment.DIRECTORY_PICTURES), albumName);
345 | if (!file.mkdirs()) {
346 | Log.e(TAG, "Directory not created");
347 | }
348 | return file;
349 | }
350 |
351 |
352 | /**
353 | * 查询指定图片
354 | *
355 | * @param context
356 | * @param path 路径
357 | * @param environmentType 沙盒类型
358 | * @param fileName 文件名
359 | * @return bitmap
360 | */
361 | public static Bitmap querySignImageBox(Context context, String path, String environmentType, String fileName) {
362 | if (TextUtils.isEmpty(fileName)) {
363 | return null;
364 | }
365 | Bitmap bitmap = null;
366 | //指定沙盒文件夹
367 | String builder = environmentType.replaceAll("/", "") + "/" +
368 | path.replaceAll("/", "") + "/" +
369 | fileName.replaceAll("/", "");
370 | File picturesFile = context.getExternalFilesDir(builder);
371 | assert picturesFile != null;
372 | if (picturesFile.exists()) {
373 | return BitmapFactory.decodeFile(picturesFile.toString());
374 | }
375 | return bitmap;
376 | }
377 |
378 | /**
379 | * 删除文件
380 | *
381 | * @param context
382 | * @param fileUri
383 | */
384 | public void delete(Context context, Uri fileUri) {
385 | context.getContentResolver().delete(fileUri, null, null);
386 | Bundle bundle;
387 | }
388 |
389 |
390 | }
391 |
--------------------------------------------------------------------------------
/net/src/main/java/com/lvhttp/net/utils/Utils.java:
--------------------------------------------------------------------------------
1 | package com.lvhttp.net.utils;
2 |
3 | import androidx.annotation.Nullable;
4 |
5 | import java.io.IOException;
6 | import java.lang.annotation.Annotation;
7 | import java.lang.reflect.Array;
8 | import java.lang.reflect.GenericArrayType;
9 | import java.lang.reflect.GenericDeclaration;
10 | import java.lang.reflect.Method;
11 | import java.lang.reflect.ParameterizedType;
12 | import java.lang.reflect.Type;
13 | import java.lang.reflect.TypeVariable;
14 | import java.lang.reflect.WildcardType;
15 | import java.util.Arrays;
16 | import java.util.NoSuchElementException;
17 | import java.util.Objects;
18 |
19 | import okhttp3.ResponseBody;
20 | import okio.Buffer;
21 |
22 |
23 | public final class Utils {
24 | static final Type[] EMPTY_TYPE_ARRAY = new Type[0];
25 |
26 | private Utils() {
27 | // No instances.
28 | }
29 |
30 |
31 | static RuntimeException methodError(Method method, String message, Object... args) {
32 | return methodError(method, null, message, args);
33 | }
34 |
35 | static RuntimeException methodError(Method method, @Nullable Throwable cause, String message,
36 | Object... args) {
37 | message = String.format(message, args);
38 | return new IllegalArgumentException(message
39 | + "\n for method "
40 | + method.getDeclaringClass().getSimpleName()
41 | + "."
42 | + method.getName(), cause);
43 | }
44 |
45 | static RuntimeException parameterError(Method method,
46 | Throwable cause, int p, String message, Object... args) {
47 | return methodError(method, cause, message + " (parameter #" + (p + 1) + ")", args);
48 | }
49 |
50 | static RuntimeException parameterError(Method method, int p, String message, Object... args) {
51 | return methodError(method, message + " (parameter #" + (p + 1) + ")", args);
52 | }
53 |
54 | static Class> getRawType(Type type) {
55 | Objects.requireNonNull(type, "type == null");
56 |
57 | if (type instanceof Class>) {
58 | // Type is a normal class.
59 | return (Class>) type;
60 | }
61 | if (type instanceof ParameterizedType) {
62 | ParameterizedType parameterizedType = (ParameterizedType) type;
63 |
64 | // I'm not exactly sure why getRawType() returns Type instead of Class. Neal isn't either but
65 | // suspects some pathological case related to nested classes exists.
66 | Type rawType;
67 | rawType = parameterizedType.getRawType();
68 | if (!(rawType instanceof Class)) {
69 | throw new IllegalArgumentException();
70 | }
71 | return (Class>) rawType;
72 | }
73 | if (type instanceof GenericArrayType) {
74 | Type componentType = ((GenericArrayType) type).getGenericComponentType();
75 | return Array.newInstance(getRawType(componentType), 0).getClass();
76 | }
77 | if (type instanceof TypeVariable) {
78 | // We could use the variable's bounds, but that won't work if there are multiple. Having a raw
79 | // type that's more general than necessary is okay.
80 | return Object.class;
81 | }
82 | if (type instanceof WildcardType) {
83 | return getRawType(((WildcardType) type).getUpperBounds()[0]);
84 | }
85 |
86 | throw new IllegalArgumentException("Expected a Class, ParameterizedType, or "
87 | + "GenericArrayType, but <" + type + "> is of type " + type.getClass().getName());
88 | }
89 |
90 | /**
91 | * Returns true if {@code a} and {@code b} are equal.
92 | */
93 | static boolean equals(Type a, Type b) {
94 | if (a == b) {
95 | return true; // Also handles (a == null && b == null).
96 |
97 | } else if (a instanceof Class) {
98 | return a.equals(b); // Class already specifies equals().
99 |
100 | } else if (a instanceof ParameterizedType) {
101 | if (!(b instanceof ParameterizedType)) {
102 | return false;
103 | }
104 | ParameterizedType pa = (ParameterizedType) a;
105 | ParameterizedType pb = (ParameterizedType) b;
106 | Object ownerA = pa.getOwnerType();
107 | Object ownerB = pb.getOwnerType();
108 | return (ownerA == ownerB || (ownerA != null && ownerA.equals(ownerB)))
109 | && pa.getRawType().equals(pb.getRawType())
110 | && Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments());
111 |
112 | } else if (a instanceof GenericArrayType) {
113 | if (!(b instanceof GenericArrayType)) {
114 | return false;
115 | }
116 | GenericArrayType ga = (GenericArrayType) a;
117 | GenericArrayType gb = (GenericArrayType) b;
118 | return equals(ga.getGenericComponentType(), gb.getGenericComponentType());
119 |
120 | } else if (a instanceof WildcardType) {
121 | if (!(b instanceof WildcardType)) {
122 | return false;
123 | }
124 | WildcardType wa = (WildcardType) a;
125 | WildcardType wb = (WildcardType) b;
126 | return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds())
127 | && Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds());
128 |
129 | } else if (a instanceof TypeVariable) {
130 | if (!(b instanceof TypeVariable)) {
131 | return false;
132 | }
133 | TypeVariable> va = (TypeVariable>) a;
134 | TypeVariable> vb = (TypeVariable>) b;
135 | return va.getGenericDeclaration() == vb.getGenericDeclaration()
136 | && va.getName().equals(vb.getName());
137 |
138 | } else {
139 | return false; // This isn't a type we support!
140 | }
141 | }
142 |
143 | /**
144 | * Returns the generic supertype for {@code supertype}. For example, given a class {@code
145 | * IntegerSet}, the result for when supertype is {@code Set.class} is {@code Set} and the
146 | * result when the supertype is {@code Collection.class} is {@code Collection}.
147 | */
148 | static Type getGenericSupertype(Type context, Class> rawType, Class> toResolve) {
149 | if (toResolve == rawType) {
150 | return context;
151 | }
152 |
153 | // We skip searching through interfaces if unknown is an interface.
154 | if (toResolve.isInterface()) {
155 | Class>[] interfaces = rawType.getInterfaces();
156 | for (int i = 0, length = interfaces.length; i < length; i++) {
157 | if (interfaces[i] == toResolve) {
158 | return rawType.getGenericInterfaces()[i];
159 | } else if (toResolve.isAssignableFrom(interfaces[i])) {
160 | return getGenericSupertype(rawType.getGenericInterfaces()[i], interfaces[i], toResolve);
161 | }
162 | }
163 | }
164 |
165 | // Check our supertypes.
166 | if (!rawType.isInterface()) {
167 | while (rawType != Object.class) {
168 | Class> rawSupertype = rawType.getSuperclass();
169 | if (rawSupertype == toResolve) {
170 | return rawType.getGenericSuperclass();
171 | } else if (toResolve.isAssignableFrom(rawSupertype)) {
172 | return getGenericSupertype(rawType.getGenericSuperclass(), rawSupertype, toResolve);
173 | }
174 | rawType = rawSupertype;
175 | }
176 | }
177 |
178 | // We can't resolve this further.
179 | return toResolve;
180 | }
181 |
182 | private static int indexOf(Object[] array, Object toFind) {
183 | for (int i = 0; i < array.length; i++) {
184 | if (toFind.equals(array[i])) {
185 | return i;
186 | }
187 | }
188 | throw new NoSuchElementException();
189 | }
190 |
191 | static String typeToString(Type type) {
192 | return type instanceof Class ? ((Class>) type).getName() : type.toString();
193 | }
194 |
195 | /**
196 | * Returns the generic form of {@code supertype}. For example, if this is {@code
197 | * ArrayList}, this returns {@code Iterable} given the input {@code
198 | * Iterable.class}.
199 | *
200 | * @param supertype a superclass of, or interface implemented by, this.
201 | */
202 | static Type getSupertype(Type context, Class> contextRawType, Class> supertype) {
203 | if (!supertype.isAssignableFrom(contextRawType)) throw new IllegalArgumentException();
204 | return resolve(context, contextRawType,
205 | getGenericSupertype(context, contextRawType, supertype));
206 | }
207 |
208 | static Type resolve(Type context, Class> contextRawType, Type toResolve) {
209 | // This implementation is made a little more complicated in an attempt to avoid object-creation.
210 | while (true) {
211 | if (toResolve instanceof TypeVariable) {
212 | TypeVariable> typeVariable = (TypeVariable>) toResolve;
213 | toResolve = resolveTypeVariable(context, contextRawType, typeVariable);
214 | if (toResolve == typeVariable) {
215 | return toResolve;
216 | }
217 |
218 | } else if (toResolve instanceof Class && ((Class>) toResolve).isArray()) {
219 | Class> original = (Class>) toResolve;
220 | Type componentType = original.getComponentType();
221 | Type newComponentType = resolve(context, contextRawType, componentType);
222 | return componentType == newComponentType ? original : new Utils.GenericArrayTypeImpl(
223 | newComponentType);
224 |
225 | } else if (toResolve instanceof GenericArrayType) {
226 | GenericArrayType original = (GenericArrayType) toResolve;
227 | Type componentType = original.getGenericComponentType();
228 | Type newComponentType = resolve(context, contextRawType, componentType);
229 | return componentType == newComponentType ? original : new Utils.GenericArrayTypeImpl(
230 | newComponentType);
231 |
232 | } else if (toResolve instanceof ParameterizedType) {
233 | ParameterizedType original = (ParameterizedType) toResolve;
234 | Type ownerType = original.getOwnerType();
235 | Type newOwnerType = resolve(context, contextRawType, ownerType);
236 | boolean changed = newOwnerType != ownerType;
237 |
238 | Type[] args = original.getActualTypeArguments();
239 | for (int t = 0, length = args.length; t < length; t++) {
240 | Type resolvedTypeArgument = resolve(context, contextRawType, args[t]);
241 | if (resolvedTypeArgument != args[t]) {
242 | if (!changed) {
243 | args = args.clone();
244 | changed = true;
245 | }
246 | args[t] = resolvedTypeArgument;
247 | }
248 | }
249 |
250 | return changed
251 | ? new Utils.ParameterizedTypeImpl(newOwnerType, original.getRawType(), args)
252 | : original;
253 |
254 | } else if (toResolve instanceof WildcardType) {
255 | WildcardType original = (WildcardType) toResolve;
256 | Type[] originalLowerBound = original.getLowerBounds();
257 | Type[] originalUpperBound = original.getUpperBounds();
258 |
259 | if (originalLowerBound.length == 1) {
260 | Type lowerBound = resolve(context, contextRawType, originalLowerBound[0]);
261 | if (lowerBound != originalLowerBound[0]) {
262 | return new Utils.WildcardTypeImpl(new Type[]{Object.class}, new Type[]{lowerBound});
263 | }
264 | } else if (originalUpperBound.length == 1) {
265 | Type upperBound = resolve(context, contextRawType, originalUpperBound[0]);
266 | if (upperBound != originalUpperBound[0]) {
267 | return new Utils.WildcardTypeImpl(new Type[]{upperBound}, EMPTY_TYPE_ARRAY);
268 | }
269 | }
270 | return original;
271 |
272 | } else {
273 | return toResolve;
274 | }
275 | }
276 | }
277 |
278 | private static Type resolveTypeVariable(
279 | Type context, Class> contextRawType, TypeVariable> unknown) {
280 | Class> declaredByRaw = declaringClassOf(unknown);
281 |
282 | // We can't reduce this further.
283 | if (declaredByRaw == null) {
284 | return unknown;
285 | }
286 |
287 | Type declaredBy = getGenericSupertype(context, contextRawType, declaredByRaw);
288 | if (declaredBy instanceof ParameterizedType) {
289 | int index = indexOf(declaredByRaw.getTypeParameters(), unknown);
290 | return ((ParameterizedType) declaredBy).getActualTypeArguments()[index];
291 | }
292 |
293 | return unknown;
294 | }
295 |
296 | /**
297 | * Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by
298 | * a class.
299 | */
300 | private static @Nullable
301 | Class> declaringClassOf(TypeVariable> typeVariable) {
302 | GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
303 | return genericDeclaration instanceof Class ? (Class>) genericDeclaration : null;
304 | }
305 |
306 | static void checkNotPrimitive(Type type) {
307 | if (type instanceof Class> && ((Class>) type).isPrimitive()) {
308 | throw new IllegalArgumentException();
309 | }
310 | }
311 |
312 | /**
313 | * Returns true if {@code annotations} contains an instance of {@code cls}.
314 | */
315 | public static boolean isAnnotationPresent(Annotation[] annotations,
316 | Class extends Annotation> cls) {
317 | for (Annotation annotation : annotations) {
318 | if (cls.isInstance(annotation)) {
319 | return true;
320 | }
321 | }
322 | return false;
323 | }
324 |
325 | static ResponseBody buffer(final ResponseBody body) throws IOException {
326 | Buffer buffer = new Buffer();
327 | body.source().readAll(buffer);
328 | return ResponseBody.create(body.contentType(), body.contentLength(), buffer);
329 | }
330 |
331 | public static Type getParameterUpperBound(int index, ParameterizedType type) {
332 | Type[] types = type.getActualTypeArguments();
333 | if (index < 0 || index >= types.length) {
334 | throw new IllegalArgumentException(
335 | "Index " + index + " not in range [0," + types.length + ") for " + type);
336 | }
337 | Type paramType = types[index];
338 | if (paramType instanceof WildcardType) {
339 | return ((WildcardType) paramType).getUpperBounds()[0];
340 | }
341 | return paramType;
342 | }
343 |
344 | static Type getParameterLowerBound(int index, ParameterizedType type) {
345 | Type paramType = type.getActualTypeArguments()[index];
346 | if (paramType instanceof WildcardType) {
347 | return ((WildcardType) paramType).getLowerBounds()[0];
348 | }
349 | return paramType;
350 | }
351 |
352 | static boolean hasUnresolvableType(@Nullable Type type) {
353 | if (type instanceof Class>) {
354 | return false;
355 | }
356 | if (type instanceof ParameterizedType) {
357 | ParameterizedType parameterizedType = (ParameterizedType) type;
358 | for (Type typeArgument : parameterizedType.getActualTypeArguments()) {
359 | if (hasUnresolvableType(typeArgument)) {
360 | return true;
361 | }
362 | }
363 | return false;
364 | }
365 | if (type instanceof GenericArrayType) {
366 | return hasUnresolvableType(((GenericArrayType) type).getGenericComponentType());
367 | }
368 | if (type instanceof TypeVariable) {
369 | return true;
370 | }
371 | if (type instanceof WildcardType) {
372 | return true;
373 | }
374 | String className = type == null ? "null" : type.getClass().getName();
375 | throw new IllegalArgumentException("Expected a Class, ParameterizedType, or "
376 | + "GenericArrayType, but <" + type + "> is of type " + className);
377 | }
378 |
379 | static final class ParameterizedTypeImpl implements ParameterizedType {
380 | private final @Nullable
381 | Type ownerType;
382 | private final Type rawType;
383 | private final Type[] typeArguments;
384 |
385 | ParameterizedTypeImpl(@Nullable Type ownerType, Type rawType, Type... typeArguments) {
386 | // Require an owner type if the raw type needs it.
387 | if (rawType instanceof Class>
388 | && (ownerType == null) != (((Class>) rawType).getEnclosingClass() == null)) {
389 | throw new IllegalArgumentException();
390 | }
391 |
392 | for (Type typeArgument : typeArguments) {
393 | Objects.requireNonNull(typeArgument, "typeArgument == null");
394 | checkNotPrimitive(typeArgument);
395 | }
396 |
397 | this.ownerType = ownerType;
398 | this.rawType = rawType;
399 | this.typeArguments = typeArguments.clone();
400 | }
401 |
402 | @Override
403 | public Type[] getActualTypeArguments() {
404 | return typeArguments.clone();
405 | }
406 |
407 | @Override
408 | public Type getRawType() {
409 | return rawType;
410 | }
411 |
412 | @Override
413 | public @Nullable
414 | Type getOwnerType() {
415 | return ownerType;
416 | }
417 |
418 | @Override
419 | public boolean equals(Object other) {
420 | return other instanceof ParameterizedType && Utils.equals(this, (ParameterizedType) other);
421 | }
422 |
423 | @Override
424 | public int hashCode() {
425 | return Arrays.hashCode(typeArguments)
426 | ^ rawType.hashCode()
427 | ^ (ownerType != null ? ownerType.hashCode() : 0);
428 | }
429 |
430 | @Override
431 | public String toString() {
432 | if (typeArguments.length == 0) {
433 | return typeToString(rawType);
434 | }
435 | StringBuilder result = new StringBuilder(30 * (typeArguments.length + 1));
436 | result.append(typeToString(rawType));
437 | result.append("<").append(typeToString(typeArguments[0]));
438 | for (int i = 1; i < typeArguments.length; i++) {
439 | result.append(", ").append(typeToString(typeArguments[i]));
440 | }
441 | return result.append(">").toString();
442 | }
443 | }
444 |
445 | private static final class GenericArrayTypeImpl implements GenericArrayType {
446 | private final Type componentType;
447 |
448 | GenericArrayTypeImpl(Type componentType) {
449 | this.componentType = componentType;
450 | }
451 |
452 | @Override
453 | public Type getGenericComponentType() {
454 | return componentType;
455 | }
456 |
457 | @Override
458 | public boolean equals(Object o) {
459 | return o instanceof GenericArrayType
460 | && Utils.equals(this, (GenericArrayType) o);
461 | }
462 |
463 | @Override
464 | public int hashCode() {
465 | return componentType.hashCode();
466 | }
467 |
468 | @Override
469 | public String toString() {
470 | return typeToString(componentType) + "[]";
471 | }
472 | }
473 |
474 | /**
475 | * The WildcardType interface supports multiple upper bounds and multiple
476 | * lower bounds. We only support what the Java 6 language needs - at most one
477 | * bound. If a lower bound is set, the upper bound must be Object.class.
478 | */
479 | private static final class WildcardTypeImpl implements WildcardType {
480 | private final Type upperBound;
481 | private final @Nullable
482 | Type lowerBound;
483 |
484 | WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
485 | if (lowerBounds.length > 1) {
486 | throw new IllegalArgumentException();
487 | }
488 | if (upperBounds.length != 1) {
489 | throw new IllegalArgumentException();
490 | }
491 |
492 | if (lowerBounds.length == 1) {
493 | if (lowerBounds[0] == null) {
494 | throw new NullPointerException();
495 | }
496 | checkNotPrimitive(lowerBounds[0]);
497 | if (upperBounds[0] != Object.class) {
498 | throw new IllegalArgumentException();
499 | }
500 | this.lowerBound = lowerBounds[0];
501 | this.upperBound = Object.class;
502 | } else {
503 | if (upperBounds[0] == null) {
504 | throw new NullPointerException();
505 | }
506 | checkNotPrimitive(upperBounds[0]);
507 | this.lowerBound = null;
508 | this.upperBound = upperBounds[0];
509 | }
510 | }
511 |
512 | @Override
513 | public Type[] getUpperBounds() {
514 | return new Type[]{upperBound};
515 | }
516 |
517 | @Override
518 | public Type[] getLowerBounds() {
519 | return lowerBound != null ? new Type[]{lowerBound} : EMPTY_TYPE_ARRAY;
520 | }
521 |
522 | @Override
523 | public boolean equals(Object other) {
524 | return other instanceof WildcardType && Utils.equals(this, (WildcardType) other);
525 | }
526 |
527 | @Override
528 | public int hashCode() {
529 | // This equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds()).
530 | return (lowerBound != null ? 31 + lowerBound.hashCode() : 1) ^ (31 + upperBound.hashCode());
531 | }
532 |
533 | @Override
534 | public String toString() {
535 | if (lowerBound != null) {
536 | return "? super " + typeToString(lowerBound);
537 | }
538 | if (upperBound == Object.class) {
539 | return "?";
540 | }
541 | return "? extends " + typeToString(upperBound);
542 | }
543 | }
544 |
545 | // https://github.com/ReactiveX/RxJava/blob/6a44e5d0543a48f1c378dc833a155f3f71333bc2/
546 | // src/main/java/io/reactivex/exceptions/Exceptions.java#L66
547 | static void throwIfFatal(Throwable t) {
548 | if (t instanceof VirtualMachineError) {
549 | throw (VirtualMachineError) t;
550 | } else if (t instanceof ThreadDeath) {
551 | throw (ThreadDeath) t;
552 | } else if (t instanceof LinkageError) {
553 | throw (LinkageError) t;
554 | }
555 | }
556 | }
--------------------------------------------------------------------------------