Decompilled method:
private static l c(String str, String str2, String str3, String str4) {
l lVar;
k kVar = (k) m.get(str);
j jVar = (j) l.get(str);
if (kVar != null) {
lVar = new l(kVar, str2, str3);
} else if (jVar != null) {
lVar = new l(jVar, str2, str3);
} else {
lVar = new l(j.GENERIC, str2, str3);
}
lVar.a(str4);
return lVar;
}
How to use Xposed to return new l (jVar, str2, str3) with its specific values?
The beginning of the code I have is:
try {
findAndHookMethod("com.xiaomi.hm.health.ui.smartplay.h", lpparam.classLoader, "c", String.class, String.class, String.class, String.class, new XC_MethodHook() {
#Override
protected void afterHookedMethod(MethodHookParam param) {
String pkg = (String) param.args[0];
if (pkg == "com.perm.kate_new_6"){
return ???;
}
}
});
} catch (Throwable t) {
t.printStackTrace();
}
The XC_MethodHook's afterHookedMethod method needs to return void. I.e., the return of the hooked method needs be be set via setResult method of XC_MethodHook instead.
Internally it sets returnEarly to true which is checked in XposedBridge, preventing the original method code from executing, as well as any other hooks on the method.
If you just want to access whatever the method was originally going to return then getResult() should do.
If you need to return anything else, you can use reflection or Xposed's helpers (findClass) to retrieve the l, k and j classes, replicate the code if needed, create a new instance and return it via setResult. Alternatively you can use a XC_MethodReplacement hook instead as you will likely replicate its functionality anyways.
Related
Usually CollectionModel will return an _embedded array, but in this example:
#GetMapping("/{id}/productMaterials")
public ResponseEntity<?> getProductMaterials(#PathVariable Integer id) {
Optional<Material> optionalMaterial = materialRepository.findById(id);
if (optionalMaterial.isPresent()) {
List<ProductMaterial> productMaterials = optionalMaterial.get().getProductMaterials();
CollectionModel<ProductMaterialModel> productMaterialModels =
new ProductMaterialModelAssembler(ProductMaterialController.class, ProductMaterialModel.class).
toCollectionModel(productMaterials);
return ResponseEntity.ok().body(productMaterialModels);
}
return ResponseEntity.badRequest().body("no such material");
}
if the productMaterials is empty CollectionModel will not render the _embedded array which will break the client. Is there any ways to fix this?
if (optionalMaterial.isPresent()) {
List<ProductMaterial> productMaterials = optionalMaterial.get().getProductMaterials();
CollectionModel<ProductMaterialModel> productMaterialModels =
new ProductMaterialModelAssembler(ProductMaterialController.class, ProductMaterialModel.class).
toCollectionModel(productMaterials);
if(productMaterialModels.isEmpty()) {
EmbeddedWrappers wrappers = new EmbeddedWrappers(false);
EmbeddedWrapper wrapper = wrappers.emptyCollectionOf(ProductMaterialModel.class);
Resources<Object> resources = new Resources<>(Arrays.asList(wrapper));
return ResponseEntity.ok(new Resources<>(resources));
} else {
return ResponseEntity.ok().body(productMaterialModels);
}
}
String type = "";
if("searchClientContactDetails".equalsIgnoreCase(methodName) || "getClientAndVendorOrgDeatilsById".equalsIgnoreCase(methodName)
|| "saveVenodrContact".equalsIgnoreCase(methodName) || "getSpocAndOwnerDetailsById".equalsIgnoreCase(methodName)
|| "terminateSpoc".equalsIgnoreCase(methodName)){
Object[] args = joinPoint.getArgs();
Object arg=args[0];
Class c=arg.getClass();
type=(String)c.getMethod("getResponderType").invoke(arg);
}
From the above code if my getResponderType value is in the args[0] then i am getting the required value , what if my value is present in args[1] or args[2] (using the same for multiple methods). In my code i will get the "getResponderType" value in first argument for few methods and in another methods i will be getting it in second or third argument.
It will be easier if you provide the methods declarations.
Because for
void searchClientContactDetails(String a, Integer b);
void searchClientContactDetails(Long a, String b);
your
Object[] args = joinPoint.getArgs();
Object arg=args[0];
will be return different values.
Maybe it is your problem )
You can iterate over the args looking for the method getResponderType. Once you find what you are looking for, you can break.
String type = "";
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
Class clazz = arg.getClass();
try {
Method mthd = arg.getClass().getMethod("getResponderType");
if (mthd != null) {
type = (String) clazz.getMethod(
"getResponderType").invoke(arg);
break;
}
} catch (Exception e) {
//do nothing
}
}
The equalsimplementatin of java.util.concurrent.ConcurrentSkipListSet in JDK is as following
public boolean equals(Object o) {
// Override AbstractSet version to avoid calling size()
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection<?> c = (Collection<?>) o;
try {
return containsAll(c) && c.containsAll(this);
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
}
But I think the code below seems to be more efficient
public boolean myEquals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection<?> c = (Collection<?>) o;
if (c.size() != this.size()) {
return false;
}
Iterator ic = c.iterator();
Iterator id = iterator();
while (ic.hasNext() && id.hasNext()) {
if (!ic.next().equals(id.next())) {
return false;
}
}
return true;
}
And a simple test is also likely supporting the second equals
public class Test {
public static void main(String[] args) {
ConcurrentSkipListSet<Integer> set1 = new ConcurrentSkipListSet<Integer>();
ConcurrentSkipListSet<Integer> set2 = new ConcurrentSkipListSet<Integer>();
for (int i = 0; i < 10000000; i++) {
set1.add(i);
set2.add(i);
}
long ts = System.currentTimeMillis();
System.out.println(set1.equals(set2));
System.out.println(System.currentTimeMillis() - ts);
ts = System.currentTimeMillis();
System.out.println(myset1.myEquals(myset2));
System.out.println(System.currentTimeMillis() - ts);
}
}
Output result
true
2713
true
589
In the JDK comment it says, This definition ensures that the equals method works properly across different implementations of the set interface. Could anyone kindly explain this?
For reference, the OpenJDK thread resulted in creating JDK-8181146 ConcurrentSkipListSet.equals efficiency.
In the JDK comment it says, This definition ensures that the equals method works properly across different implementations of the set interface. Could anyone kindly explain this?
It comes from Set.equals(Object). Per the documentation:
Returns true if the specified object is also a set, the two sets have the same size, and every member of the specified set is contained in this set (or equivalently, every member of this set is contained in the specified set). This definition ensures that the equals method works properly across different implementations of the set interface.
It is implying that Set.equals implementations should be defined by the behavior of Set.contains(Object). Which then leads you to this verbiage from the java.util.SortedSet:
Note that the ordering maintained by a sorted set (whether or not an explicit comparator is provided) must be consistent with equals if the sorted set is to correctly implement the Set interface. (See the Comparable interface or Comparator interface for a precise definition of consistent with equals.) This is so because the Set interface is defined in terms of the equals operation, but a sorted set performs all element comparisons using its compareTo (or compare) method, so two elements that are deemed equal by this method are, from the standpoint of the sorted set, equal. The behavior of a sorted set is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Set interface.
So why the 'this contains that and that contains this' in the ConcurrentSkipListSet? First off you want to avoid the call to ConcurrentSkipListSet.size() because:
Beware that, unlike in most collections, this method is NOT a constant-time operation. Because of the asynchronous nature of these sets, determining the current number of elements requires traversing them all to count them. Additionally, it is possible for the size to change during execution of this method, in which case the returned result will be inaccurate. Thus, this method is typically not very useful in concurrent applications.
The second reason is that you want to be 'consistent with equals'.
Let's make a cruel example based off your code:
private static boolean myEquals(Set o1, Set o2) {
if (o1.size() == 1 && o2.size() == 1) {
Iterator ic = o2.iterator();
Iterator id = o1.iterator();
while (ic.hasNext() && id.hasNext()) {
if (!ic.next().equals(id.next())) {
return false;
}
}
return true;
}
return o1.equals(o2);
}
public static void main(String[] args) {
print(skiplist(new BigDecimal("1.0")), tree(new BigDecimal("1.00")));
print(skiplist(new BigDecimal("1.0")), hash(new BigDecimal("1.00")));
print(skiplist(new BigDecimal("1.0")), identity(new BigDecimal("1.00")));
print(skiplist(BigDecimal.ONE), identity(new BigDecimal(BigInteger.ONE, 0)));
}
private static Collection<BigDecimal> e() {
return Arrays.asList(new BigDecimal("1.0"));
}
private static <E> Set<E> hash(E... e) {
return new HashSet<>(Arrays.asList(e));
}
private static <E> Set<E> skiplist(E... e) {
return new ConcurrentSkipListSet<>(Arrays.asList(e));
}
private static <E> Set<E> tree(E... e) {
return new TreeSet<>(Arrays.asList(e));
}
private static <E> Set<E> identity(E... e) {
Set<E> s = Collections.newSetFromMap(new IdentityHashMap<E, Boolean>());
Collections.addAll(s, e);
return s;
}
private static void print(Set o1, Set o2) {
System.out.println(o1.getClass().getName()
+ "==" + o2.getClass().getName() + ": "
+ o1.equals(o2) + ": " + myEquals(o1, o2));
System.out.println(o2.getClass().getName()
+ "==" + o1.getClass().getName() + ": " + o2.equals(o1)
+ ": " + myEquals(o2, o1));
}
Which outputs:
java.util.concurrent.ConcurrentSkipListSet==java.util.TreeSet: true: false
java.util.TreeSet==java.util.concurrent.ConcurrentSkipListSet: true: false
java.util.concurrent.ConcurrentSkipListSet==java.util.HashSet: false: false
java.util.HashSet==java.util.concurrent.ConcurrentSkipListSet: false: false
java.util.concurrent.ConcurrentSkipListSet==java.util.Collections$SetFromMap: false: false
java.util.Collections$SetFromMap==java.util.concurrent.ConcurrentSkipListSet: false: false
java.util.concurrent.ConcurrentSkipListSet==java.util.Collections$SetFromMap: false: true
java.util.Collections$SetFromMap==java.util.concurrent.ConcurrentSkipListSet: false: true
That output shows that the new implementation would not be consistent with equals:
The natural ordering for a class C is said to be consistent with equals if and only if e1.compareTo(e2) == 0 has the same boolean value as e1.equals(e2) for every e1 and e2 of class C. Note that null is not an instance of any class, and e.compareTo(null) should throw a NullPointerException even though e.equals(null) returns false.
Now we could fix this by replacing the element check with
((Comparable) e1).compareTo((Comparable) e2) != 0 or Comparator.compare(e1, e2) != 0 and add checks to try to determine that the two sets use the same ordering but keep in mind that collections can be wrapped and there is nothing stopping a caller from hiding the fact that a set is backed by sorted set. Now you are back to the 'this contains that and that contains this' implementation of equals which can deal with collection wrappers.
Another nice property of the 'this contains that and that contains this' implementation is that the equals implementation is not creating an iterator object for the given collection which in the worst case could have an implementation like Arrays.asList(s.toArray()).iterator() under the hood.
Without relaxing spec, relaxing the existing behavior, or adding a collection method that returns a BiPredicate to capture the 'equivalence relationship' for a collection, I think it will be hard to add an optimization like this to the JDK.
Trying to save selections from a CheckBoxList as a comma-separated list (string) in DB (one or more choices selected). I am using a proxy in order to save as a string because otherwise I'd have to create separate tables in the DB for a relation - the work is not worth it for this simple scenario and I was hoping that I could just convert it to a string and avoid that.
The CheckBoxList uses an enum for it's choices:
public enum Selection
{
Selection1,
Selection2,
Selection3
}
Not to be convoluted, but I use [Display(Name="Choice 1")] and an extension class to display something friendly on the UI. Not sure if I can save that string instead of just the enum, although I think if I save as enum it's not a big deal for me to "display" the friendly string on UI on some confirmation page.
This is the "Record" class that saves a string in the DB:
public virtual string MyCheckBox { get; set; }
This is the "Proxy", which is some sample I found but not directly dealing with enum, and which uses IEnumerable<string> (or should it be IEnumerable<Selection>?):
public IEnumerable<string> MyCheckBox
{
get
{
if (String.IsNullOrWhiteSpace(Record.MyCheckBox)) return new string[] { };
return Record
.MyCheckBox
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(r => r.Trim())
.Where(r => !String.IsNullOrEmpty(r));
}
set
{
Record.MyCheckBox = value == null ? null : String.Join(",", value);
}
}
To save in the DB, I am trying to do this in a create class:
proxy.MyCheckBox = record.MyCheckBox; //getting error here
but am getting the error:
Cannot implicitly convert 'string' to System.Collections.Generic.IEnumerable'
I don't know, if it's possible or better, to use Parse or ToString from the API for enum values.
I know that doing something like this will store whatever I put in the ("") into the DB, so it's just a matter of figuring out how to overcome the error (or, if there is an alternative):
proxy.MyCheckBox = new[] {"foo", "bar"};
I am not good with this stuff and have just been digging and digging to come up with a solution. Any help is much appreciated.
You can accomplish this using a custom user type. The example below uses an ISet<string> on the class and stores the values as a delimited string.
[Serializable]
public class CommaDelimitedSet : IUserType
{
const string delimiter = ",";
#region IUserType Members
public new bool Equals(object x, object y)
{
if (ReferenceEquals(x, y))
{
return true;
}
var xSet = x as ISet<string>;
var ySet = y as ISet<string>;
if (xSet == null || ySet == null)
{
return false;
}
// compare set contents
return xSet.Except(ySet).Count() == 0 && ySet.Except(xSet).Count() == 0;
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
var outValue = NHibernateUtil.String.NullSafeGet(rs, names[0]) as string;
if (string.IsNullOrEmpty(outValue))
{
return new HashSet<string>();
}
else
{
var splitArray = outValue.Split(new[] {Delimiter}, StringSplitOptions.RemoveEmptyEntries);
return new HashSet<string>(splitArray);
}
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
var inValue = value as ISet<string>;
object setValue = inValue == null ? null : string.Join(Delimiter, inValue);
NHibernateUtil.String.NullSafeSet(cmd, setValue, index);
}
public object DeepCopy(object value)
{
// return new ISet so that Equals can work
// see http://www.mail-archive.com/nhusers#googlegroups.com/msg11054.html
var set = value as ISet<string>;
if (set == null)
{
return null;
}
return new HashSet<string>(set);
}
public object Replace(object original, object target, object owner)
{
return original;
}
public object Assemble(object cached, object owner)
{
return DeepCopy(cached);
}
public object Disassemble(object value)
{
return DeepCopy(value);
}
public SqlType[] SqlTypes
{
get { return new[] {new SqlType(DbType.String)}; }
}
public Type ReturnedType
{
get { return typeof(ISet<string>); }
}
public bool IsMutable
{
get { return false; }
}
#endregion
}
Usage in mapping file:
Map(x => x.CheckboxValues.CustomType<CommaDelimitedSet>();
SingleOrDefault returns null, but what if I want to assign values to represent the object that wasn't found?
you can do something like
myStrings.DefaultIfEmpty("myDefaultString").Single()
check out here
?? operator. If the left argument is null, evaluate and return the second argument.
myCollection.SingleOrDefault() ?? new[]{new Item(...)}
This will only work with reference types (or nullables), but it would do what you're looking for very simply.
You could roll your own.
public static T SingleOrDefault<T>(this IEnumerable<T> enumerable, T defaultValue) {
if ( 1 != enumerable.Count() ) {
return defaultValue;
}
return enumerable.Single();
}
This can be a bit expensive though because Count() requires you to process the entire collection and can be fairly expensive to run. It would be better to either call Single, catch the InvalidOperationException or roll a IsSingle method
public static bool IsSingle<T>(this IEnumerable<T> enumerable) {
using ( var e = enumerable.GetEnumerator() ) {
return e.MoveNext() && !e.MoveNext();
}
}
public static T SingleOrDefault<T>(this IEnumerable<T> enumerable, T defaultValue) {
if ( !enumerable.IsSingle() ) {
if( enumerable.IsEmpty() ) {
return defaultValue;
}
throw new InvalidOperationException("More than one element");
}
return enumerable.Single();
}
You could create your own extension methods -- SingleOrNew.
public static class IEnumerableExtensions
{
public static T SingleOrNew<T>( this IEnumerable<T> enumeration, T newValue )
{
T elem = enumeration.SingleOrDefault();
if (elem == null)
{
return newValue;
}
return elem;
}
public static T SingleOrNew<T>( this IEnumerable<T> enumeration, Func<T,bool> predicate, T newValue )
{
T elem = enumeration.SingleOrDefault( predicate );
if (elem == null)
{
return newValue;
}
return elem;
}
}