12cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok/*
22cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok * Copyright (C) 2011 The Android Open Source Project
32cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok *
42cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok * Licensed under the Apache License, Version 2.0 (the "License");
52cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok * you may not use this file except in compliance with the License.
62cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok * You may obtain a copy of the License at
72cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok *
82cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok *      http://www.apache.org/licenses/LICENSE-2.0
92cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok *
102cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok * Unless required by applicable law or agreed to in writing, software
112cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok * distributed under the License is distributed on an "AS IS" BASIS,
122cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok * See the License for the specific language governing permissions and
142cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok * limitations under the License.
152cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok */
162cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok
172cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatokpackage com.android.inputmethod.compat;
182cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok
192cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatokimport android.text.TextUtils;
20610f1dc8553cf2ed97e763a06a19380c4a6cd636satokimport android.util.Log;
21610f1dc8553cf2ed97e763a06a19380c4a6cd636satok
22b2707856aba4fc9b063f26305f1fb603b19c1701satokimport java.lang.reflect.Constructor;
2324119dfd414f948b2da214c021cc76e06b3dd41csatokimport java.lang.reflect.Field;
2403a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaokaimport java.lang.reflect.InvocationTargetException;
25610f1dc8553cf2ed97e763a06a19380c4a6cd636satokimport java.lang.reflect.Method;
262cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok
271e11c44d1b5f9ddf593c5407cb14c458be0056f2Tadashi G. Takaokapublic final class CompatUtils {
28610f1dc8553cf2ed97e763a06a19380c4a6cd636satok    private static final String TAG = CompatUtils.class.getSimpleName();
292cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok
30298b4240f84344bbcd70b72dfb20806ba93ffd22Tadashi G. Takaoka    private CompatUtils() {
31298b4240f84344bbcd70b72dfb20806ba93ffd22Tadashi G. Takaoka        // This utility class is not publicly instantiable.
322cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok    }
33610f1dc8553cf2ed97e763a06a19380c4a6cd636satok
34b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa    public static Class<?> getClass(final String className) {
35610f1dc8553cf2ed97e763a06a19380c4a6cd636satok        try {
36610f1dc8553cf2ed97e763a06a19380c4a6cd636satok            return Class.forName(className);
3703a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        } catch (final ClassNotFoundException e) {
38610f1dc8553cf2ed97e763a06a19380c4a6cd636satok            return null;
39610f1dc8553cf2ed97e763a06a19380c4a6cd636satok        }
40610f1dc8553cf2ed97e763a06a19380c4a6cd636satok    }
41610f1dc8553cf2ed97e763a06a19380c4a6cd636satok
42b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa    public static Method getMethod(final Class<?> targetClass, final String name,
43b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa            final Class<?>... parameterTypes) {
4403a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        if (targetClass == null || TextUtils.isEmpty(name)) {
4503a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka            return null;
4603a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        }
47610f1dc8553cf2ed97e763a06a19380c4a6cd636satok        try {
48610f1dc8553cf2ed97e763a06a19380c4a6cd636satok            return targetClass.getMethod(name, parameterTypes);
4903a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        } catch (final SecurityException | NoSuchMethodException e) {
50610f1dc8553cf2ed97e763a06a19380c4a6cd636satok            // ignore
51610f1dc8553cf2ed97e763a06a19380c4a6cd636satok        }
521fef530ec7626fa16777f52b48191e61db8f46d4satok        return null;
53610f1dc8553cf2ed97e763a06a19380c4a6cd636satok    }
54610f1dc8553cf2ed97e763a06a19380c4a6cd636satok
55b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa    public static Field getField(final Class<?> targetClass, final String name) {
5603a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        if (targetClass == null || TextUtils.isEmpty(name)) {
5703a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka            return null;
5803a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        }
5924119dfd414f948b2da214c021cc76e06b3dd41csatok        try {
6024119dfd414f948b2da214c021cc76e06b3dd41csatok            return targetClass.getField(name);
6103a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        } catch (final SecurityException | NoSuchFieldException e) {
6224119dfd414f948b2da214c021cc76e06b3dd41csatok            // ignore
6324119dfd414f948b2da214c021cc76e06b3dd41csatok        }
641fef530ec7626fa16777f52b48191e61db8f46d4satok        return null;
6524119dfd414f948b2da214c021cc76e06b3dd41csatok    }
6624119dfd414f948b2da214c021cc76e06b3dd41csatok
67b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa    public static Constructor<?> getConstructor(final Class<?> targetClass,
68b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa            final Class<?> ... types) {
6903a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        if (targetClass == null || types == null) {
7003a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka            return null;
7103a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        }
72b2707856aba4fc9b063f26305f1fb603b19c1701satok        try {
73b2707856aba4fc9b063f26305f1fb603b19c1701satok            return targetClass.getConstructor(types);
7403a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        } catch (final SecurityException | NoSuchMethodException e) {
75b2707856aba4fc9b063f26305f1fb603b19c1701satok            // ignore
76b2707856aba4fc9b063f26305f1fb603b19c1701satok        }
771fef530ec7626fa16777f52b48191e61db8f46d4satok        return null;
781fef530ec7626fa16777f52b48191e61db8f46d4satok    }
791fef530ec7626fa16777f52b48191e61db8f46d4satok
80b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa    public static Object newInstance(final Constructor<?> constructor, final Object ... args) {
8103a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        if (constructor == null) {
8203a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka            return null;
8303a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        }
841fef530ec7626fa16777f52b48191e61db8f46d4satok        try {
851fef530ec7626fa16777f52b48191e61db8f46d4satok            return constructor.newInstance(args);
8603a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        } catch (final InstantiationException | IllegalAccessException | IllegalArgumentException
8703a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka                | InvocationTargetException e) {
88a8d3d50970cecf961940efc0e908fbb2f9b35472Ken Wakasa            Log.e(TAG, "Exception in newInstance", e);
891fef530ec7626fa16777f52b48191e61db8f46d4satok        }
901fef530ec7626fa16777f52b48191e61db8f46d4satok        return null;
91b2707856aba4fc9b063f26305f1fb603b19c1701satok    }
92b2707856aba4fc9b063f26305f1fb603b19c1701satok
93b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa    public static Object invoke(final Object receiver, final Object defaultValue,
94b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa            final Method method, final Object... args) {
9503a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        if (method == null) {
9603a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka            return defaultValue;
9703a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        }
98610f1dc8553cf2ed97e763a06a19380c4a6cd636satok        try {
99610f1dc8553cf2ed97e763a06a19380c4a6cd636satok            return method.invoke(receiver, args);
10003a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        } catch (final IllegalAccessException | IllegalArgumentException
10103a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka                | InvocationTargetException e) {
102a8d3d50970cecf961940efc0e908fbb2f9b35472Ken Wakasa            Log.e(TAG, "Exception in invoke", e);
103610f1dc8553cf2ed97e763a06a19380c4a6cd636satok        }
1041fef530ec7626fa16777f52b48191e61db8f46d4satok        return defaultValue;
105610f1dc8553cf2ed97e763a06a19380c4a6cd636satok    }
106610f1dc8553cf2ed97e763a06a19380c4a6cd636satok
107b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa    public static Object getFieldValue(final Object receiver, final Object defaultValue,
108b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa            final Field field) {
10903a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        if (field == null) {
11003a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka            return defaultValue;
11103a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        }
11224119dfd414f948b2da214c021cc76e06b3dd41csatok        try {
11324119dfd414f948b2da214c021cc76e06b3dd41csatok            return field.get(receiver);
11403a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        } catch (final IllegalAccessException | IllegalArgumentException e) {
115a8d3d50970cecf961940efc0e908fbb2f9b35472Ken Wakasa            Log.e(TAG, "Exception in getFieldValue", e);
11624119dfd414f948b2da214c021cc76e06b3dd41csatok        }
1171fef530ec7626fa16777f52b48191e61db8f46d4satok        return defaultValue;
11824119dfd414f948b2da214c021cc76e06b3dd41csatok    }
11924119dfd414f948b2da214c021cc76e06b3dd41csatok
120b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa    public static void setFieldValue(final Object receiver, final Field field, final Object value) {
12103a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        if (field == null) {
12203a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka            return;
12303a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        }
12424119dfd414f948b2da214c021cc76e06b3dd41csatok        try {
12524119dfd414f948b2da214c021cc76e06b3dd41csatok            field.set(receiver, value);
12603a1c442aeddbd511be92e11465b6206d2ca976cTadashi G. Takaoka        } catch (final IllegalAccessException | IllegalArgumentException e) {
127a8d3d50970cecf961940efc0e908fbb2f9b35472Ken Wakasa            Log.e(TAG, "Exception in setFieldValue", e);
12824119dfd414f948b2da214c021cc76e06b3dd41csatok        }
12924119dfd414f948b2da214c021cc76e06b3dd41csatok    }
130fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa
131fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa    public static ClassWrapper getClassWrapper(final String className) {
132fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        return new ClassWrapper(getClass(className));
133fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa    }
134fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa
135fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa    public static final class ClassWrapper {
136fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        private final Class<?> mClass;
137fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        public ClassWrapper(final Class<?> targetClass) {
138fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa            mClass = targetClass;
139fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        }
140fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa
141fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        public boolean exists() {
142fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa            return mClass != null;
143fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        }
144fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa
145fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        public <T> ToObjectMethodWrapper<T> getMethod(final String name,
146fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa                final T defaultValue, final Class<?>... parameterTypes) {
1475f00fe09e9a611b647592188316e5999465df4d3Tadashi G. Takaoka            return new ToObjectMethodWrapper<>(CompatUtils.getMethod(mClass, name, parameterTypes),
148fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa                    defaultValue);
149fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        }
150fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa
151fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        public ToIntMethodWrapper getPrimitiveMethod(final String name, final int defaultValue,
152fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa                final Class<?>... parameterTypes) {
153fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa            return new ToIntMethodWrapper(CompatUtils.getMethod(mClass, name, parameterTypes),
154fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa                    defaultValue);
155fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        }
156fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa
157fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        public ToFloatMethodWrapper getPrimitiveMethod(final String name, final float defaultValue,
158fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa                final Class<?>... parameterTypes) {
159fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa            return new ToFloatMethodWrapper(CompatUtils.getMethod(mClass, name, parameterTypes),
160fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa                    defaultValue);
161fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        }
162fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa
163fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        public ToBooleanMethodWrapper getPrimitiveMethod(final String name,
164fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa                final boolean defaultValue, final Class<?>... parameterTypes) {
165fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa            return new ToBooleanMethodWrapper(CompatUtils.getMethod(mClass, name, parameterTypes),
166fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa                    defaultValue);
167fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        }
168fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa    }
169fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa
170fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa    public static final class ToObjectMethodWrapper<T> {
171fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        private final Method mMethod;
172fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        private final T mDefaultValue;
173fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        public ToObjectMethodWrapper(final Method method, final T defaultValue) {
174fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa            mMethod = method;
175fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa            mDefaultValue = defaultValue;
176fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        }
177fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        @SuppressWarnings("unchecked")
178fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        public T invoke(final Object receiver, final Object... args) {
179fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa            return (T) CompatUtils.invoke(receiver, mDefaultValue, mMethod, args);
180fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        }
181fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa    }
182fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa
183fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa    public static final class ToIntMethodWrapper {
184fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        private final Method mMethod;
185fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        private final int mDefaultValue;
186fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        public ToIntMethodWrapper(final Method method, final int defaultValue) {
187fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa            mMethod = method;
188fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa            mDefaultValue = defaultValue;
189fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        }
190fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        public int invoke(final Object receiver, final Object... args) {
191fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa            return (int) CompatUtils.invoke(receiver, mDefaultValue, mMethod, args);
192fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        }
193fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa    }
194fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa
195fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa    public static final class ToFloatMethodWrapper {
196fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        private final Method mMethod;
197fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        private final float mDefaultValue;
198fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        public ToFloatMethodWrapper(final Method method, final float defaultValue) {
199fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa            mMethod = method;
200fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa            mDefaultValue = defaultValue;
201fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        }
202fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        public float invoke(final Object receiver, final Object... args) {
203fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa            return (float) CompatUtils.invoke(receiver, mDefaultValue, mMethod, args);
204fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        }
205fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa    }
206fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa
207fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa    public static final class ToBooleanMethodWrapper {
208fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        private final Method mMethod;
209fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        private final boolean mDefaultValue;
210fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        public ToBooleanMethodWrapper(final Method method, final boolean defaultValue) {
211fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa            mMethod = method;
212fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa            mDefaultValue = defaultValue;
213fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        }
214fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        public boolean invoke(final Object receiver, final Object... args) {
215fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa            return (boolean) CompatUtils.invoke(receiver, mDefaultValue, mMethod, args);
216fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa        }
217fa39ffcf8461ad6e53a86d8957ab892a3e367d16Yohei Yukawa    }
2182cff4d7e4abdb192151f9b4027fc93fe28a8bdaasatok}
219