/**
* OpenAtlasForAndroid Project
* The MIT License (MIT) Copyright (OpenAtlasForAndroid) 2015 Bunny Blue,achellies
* <p>
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
* <p>
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
* <p>
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @author BunnyBlue
**/
package com.openatlas.hack;
import com.openatlas.hack.Hack.HackDeclaration.HackAssertionException;
import com.openatlas.hack.Interception.InterceptionHandler;
import com.openatlas.runtime.DelegateClassLoader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Hack {
private static AssertionFailureHandler sFailureHandler;
public interface AssertionFailureHandler {
boolean onAssertionFailure(HackAssertionException hackAssertionException);
}
public static abstract class HackDeclaration {
public static class HackAssertionException extends Throwable {
private static final long serialVersionUID = 1;
private Class<?> mHackedClass;
private String mHackedFieldName;
private String mHackedMethodName;
public HackAssertionException(String str) {
super(str);
}
public HackAssertionException(Exception exception) {
super(exception);
}
@Override
public String toString() {
return getCause() != null ? getClass().getName() + ": "
+ getCause() : super.toString();
}
public Class<?> getHackedClass() {
return this.mHackedClass;
}
public void setHackedClass(Class<?> cls) {
this.mHackedClass = cls;
}
public String getHackedMethodName() {
return this.mHackedMethodName;
}
public void setHackedMethodName(String str) {
this.mHackedMethodName = str;
}
public String getHackedFieldName() {
return this.mHackedFieldName;
}
public void setHackedFieldName(String str) {
this.mHackedFieldName = str;
}
}
}
public static class HackedClass<C> {
protected Class<C> mClass;
public HackedField<C, Object> staticField(String str)
throws HackAssertionException {
return new HackedField(this.mClass, str, 8);
}
public HackedField<C, Object> field(String str)
throws HackAssertionException {
return new HackedField(this.mClass, str, 0);
}
public HackedMethod staticMethod(String str, Class<?>... clsArr)
throws HackAssertionException {
return new HackedMethod(this.mClass, str, clsArr, 8);
}
public HackedMethod method(String str, Class<?>... clsArr)
throws HackAssertionException {
return new HackedMethod(this.mClass, str, clsArr, 0);
}
public HackedConstructor constructor(Class<?>... clsArr)
throws HackAssertionException {
return new HackedConstructor(this.mClass, clsArr);
}
public HackedClass(Class<C> cls) {
this.mClass = cls;
}
public Class<C> getmClass() {
return this.mClass;
}
}
public static class HackedConstructor {
protected Constructor<?> mConstructor;
HackedConstructor(Class<?> cls, Class<?>[] clsArr)
throws HackAssertionException {
if (cls != null) {
try {
this.mConstructor = cls.getDeclaredConstructor(clsArr);
} catch (Exception e) {
HackAssertionException hackAssertionException = new HackAssertionException(
e);
hackAssertionException.setHackedClass(cls);
Hack.fail(hackAssertionException);
}
}
}
public Object getInstance(Object... objArr)
throws IllegalArgumentException {
Object obj = null;
this.mConstructor.setAccessible(true);
try {
obj = this.mConstructor.newInstance(objArr);
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
public static class HackedField<C, T> {
private final Field mField;
public <T2> com.openatlas.hack.Hack.HackedField<C, T2> ofGenericType(
Class<?> cls) throws HackAssertionException {
if (!(this.mField == null || cls.isAssignableFrom(this.mField
.getType()))) {
Hack.fail(new HackAssertionException(new ClassCastException(
this.mField + " is not of type " + cls)));
}
return (HackedField<C, T2>) this;
}
public <T2> com.openatlas.hack.Hack.HackedField<C, T2> ofType(
Class<T2> cls) throws HackAssertionException {
if (!(this.mField == null || cls.isAssignableFrom(this.mField
.getType()))) {
Hack.fail(new HackAssertionException(new ClassCastException(
this.mField + " is not of type " + cls)));
}
return (HackedField<C, T2>) this;
}
public com.openatlas.hack.Hack.HackedField<C, T> ofType(
String str) throws HackAssertionException {
com.openatlas.hack.Hack.HackedField<C, T> ofType = null;
try {
ofType = (HackedField<C, T>) ofType(Class.forName(str));
} catch (Exception e) {
Hack.fail(new HackAssertionException(e));
}
return ofType;
}
public T get(C c) {
try {
return (T) this.mField.get(c);
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
public void set(C c, Object obj) {
try {
this.mField.set(c, obj);
} catch (Throwable e) {
e.printStackTrace();
if (obj instanceof DelegateClassLoader) {
throw new RuntimeException("set DelegateClassLoader fail",
e);
}
}
}
public void hijack(C c, InterceptionHandler<?> interceptionHandler) {
Object obj = get(c);
if (obj == null) {
throw new IllegalStateException("Cannot hijack null");
}
set(c, Interception.proxy(obj,
interceptionHandler, obj.getClass()
.getInterfaces()));
}
HackedField(Class<C> cls, String str, int i)
throws HackAssertionException {
Field field = null;
if (cls == null) {
this.mField = null;
return;
}
try {
field = cls.getDeclaredField(str);
if (i > 0 && (field.getModifiers() & i) != i) {
Hack.fail(new HackAssertionException(field
+ " does not match modifiers: " + i));
}
field.setAccessible(true);
} catch (Exception e) {
HackAssertionException hackAssertionException = new HackAssertionException(
e);
hackAssertionException.setHackedClass(cls);
hackAssertionException.setHackedFieldName(str);
Hack.fail(hackAssertionException);
} finally {
this.mField = field;
}
}
public Field getField() {
return this.mField;
}
}
public static class HackedMethod {
protected final Method mMethod;
public Object invoke(Object obj, Object... objArr)
throws IllegalArgumentException, InvocationTargetException {
Object obj2 = null;
try {
obj2 = this.mMethod.invoke(obj, objArr);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return obj2;
}
HackedMethod(Class<?> cls, String str, Class<?>[] clsArr, int i)
throws HackAssertionException {
Method method = null;
if (cls == null) {
this.mMethod = null;
return;
}
try {
method = cls.getDeclaredMethod(str, clsArr);
if (i > 0 && (method.getModifiers() & i) != i) {
Hack.fail(new HackAssertionException(method
+ " does not match modifiers: " + i));
}
method.setAccessible(true);
} catch (Exception e) {
HackAssertionException hackAssertionException = new HackAssertionException(
e);
hackAssertionException.setHackedClass(cls);
hackAssertionException.setHackedMethodName(str);
Hack.fail(hackAssertionException);
} finally {
this.mMethod = method;
}
}
public Method getMethod() {
return this.mMethod;
}
}
public static <T> HackedClass<T> into(Class<T> cls) {
return new HackedClass(cls);
}
public static <T> HackedClass<T> into(String str)
throws HackAssertionException {
try {
return new HackedClass(Class.forName(str));
} catch (Exception e) {
fail(new HackAssertionException(e));
return new HackedClass(null);
}
}
private static void fail(HackAssertionException hackAssertionException)
throws HackAssertionException {
if (sFailureHandler == null
|| !sFailureHandler.onAssertionFailure(hackAssertionException)) {
throw hackAssertionException;
}
}
public static void setAssertionFailureHandler(
AssertionFailureHandler assertionFailureHandler) {
sFailureHandler = assertionFailureHandler;
}
private Hack() {
}
}