Spring SpEL chooses wrong method to invoke - spring

I'm trying to evaluate the following SpEL expression (Spring-expression version 3.1.1):
T(com.google.common.collect.Lists).newArrayList(#iterable)
where #iterable is of type java.lang.Iterable.
Google Guava com.google.common.collect.Lists (version 14.0) does have a method newArrayList(Iterable) but for some reason SpEL chooses to invoke a different method: newArrayList(Object[])
I dived into the code and found the issue to be with org.springframework.expression.spel.support.ReflectiveMethodResolver implementation: it seems to be sensitive to the manner in which methods are sorted by the java.lang.Class::getMethods.
If 2 methods match the invocation (in the case one of the methods is varargs), the later method (in the order) will be invoked, instead of choosing the method that isn't varargs (which is more specific).
It seems like JDK doesn't guarantee the order the methods are sorted: different runs show different order.
Is there a way to overcome this issue?

You can use the collection projections of Spring EL to select all from iterable and convert it to list:
"#iterable.?[true]"
A simple example to test:
Iterable<Integer> it = () -> new Iterator<Integer>() {
private int[] a = new int[]{1, 2, 3};
private int index = 0;
#Override
public boolean hasNext() {
return index < a.length;
}
#Override
public Integer next() {
return a[index++];
}
};
Tmp tmp = new Tmp();
tmp.setO(it);
StandardEvaluationContext context = new StandardEvaluationContext(tmp);
ArrayList<Integer> list = parser.parseExpression("o.?[true]").getValue(context,
ArrayList.class);

Related

Use hashCode() for sorting Objects in java, not in HashTable and ect

I need your help.
If i want to sort a PriorityQeueu in java, with out connection to it's attributes - could i use the hashCode's Objects to compare?
This how i did it:
comp = new Comparator<Person>() {
#Override
public int compare(Person p1, Person p2) {
if(p1.hashCode() < p2.hashCode()) return 1;
if(p1.hashCode() == p2.hashCode()) return 0;
return -1;
}
};
collector = new PriorityQueue<Person>(comp);
It doesn't sound like a good approach.
Default hashCode() is typically implemented by converting the internal address of the object into an integer. So the order of objects will differ between application executions.
Also, 2 objects with the same set of attribute values will not return the same hashCode value unless you override the implementation. This actually breaks the expected contract of Comparable.

Using org.xmlunit.diff.NodeFilters in XMLUnit DiffBuilder

I am using the XMLUnit in JUnit to compare the results of tests. I have a problem wherein there is an Element in my XML which gets the CURRENT TIMESTAMP as the tests run and when compared with the expected output, the results will never match.
To overcome this, I read about using org.xmlunit.diff.NodeFilters, but do not have any examples on how to implement this. The code snippet I have is as below,
final org.xmlunit.diff.Diff documentDiff = DiffBuilder
.compare(sourcExp)
.withTest(sourceActual)
.ignoreComments()
.ignoreWhitespace()
//.withNodeFilter(Node.ELEMENT_NODE)
.build();
return documentDiff.hasDifferences();
My problem is, how do I implement the NodeFilter? What parameter should be passed and should that be passed? There are no samples on this. The NodeFilter method gets Predicate<Node> as the IN parameter. What does Predicate<Node> mean?
Predicate is a functional interface with a single test method that - in the case of NodeFilter receives a DOM Node as argument and returns a boolean. javadoc of Predicate
An implementation of Predicate<Node> can be used to filter nodes for the difference engine and only those Nodes for which the Predicate returns true will be compared. javadoc of setNodeFilter, User-Guide
Assuming your element containing the timestamp was called timestamp you'd use something like
.withNodeFilter(new Predicate<Node>() {
#Override
public boolean test(Node n) {
return !(n instanceof Element &&
"timestamp".equals(Nodes.getQName(n).getLocalPart()));
}
})
or using lambdas
.withNodeFilter(n -> !(n instanceof Element &&
"timestamp".equals(Nodes.getQName(n).getLocalPart())))
This uses XMLUnit's org.xmlunit.util.Nodes to get the element name more easily.
The below code worked for me,
public final class IgnoreNamedElementsDifferenceListener implements
DifferenceListener {
private Set<String> blackList = new HashSet<String>();
public IgnoreNamedElementsDifferenceListener(String... elementNames) {
for (String name : elementNames) {
blackList.add(name);
}
}
public int differenceFound(Difference difference) {
if (difference.getId() == DifferenceConstants.TEXT_VALUE_ID) {
if (blackList.contains(difference.getControlNodeDetail().getNode()
.getParentNode().getNodeName())) {
return DifferenceListener.RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
}
}
return DifferenceListener.RETURN_ACCEPT_DIFFERENCE;
}
public void skippedComparison(Node node, Node node1) {
}

collection sorting

The GDK docs indicate that Collection.sort(Comparator comparator) does not change the collection it is called on, but the code below indicates otherwise. Is this a bug in the implementation, error in the docs, or a misunderstanding on my part?
class ISO3LangComparator implements Comparator<Locale> {
int compare(Locale locale1, Locale locale2) {
locale1.ISO3Language <=> locale2.ISO3Language
}
}
List<Locale> locales = [Locale.FRENCH, Locale.ENGLISH]
def sortedLocales = locales.sort(new ISO3LangComparator())
// This assertion fails
assert locales[0] == frenchLocale
the documentation states:
If the Collection is a List, it is
sorted in place and returned.
Otherwise, the elements are first
placed into a new list which is then
sorted and returned - leaving the
original Collection unchanged.
which is reflected in the implementation of the sort() method
public static <T> List<T> sort(Collection<T> self, Comparator<T> comparator) {
List<T> list = asList(self);
Collections.sort(list, comparator);
return list;
}
the asList method looks whether the given collection is an instanceof java.util.List. If yes, it returns the reference, if not it returns a new java.util.ArrayList instance.
since you are using the [] syntax you are implicitly working with an instance of java.util.List.

Can I use Action<T> to perform a recursive search and return an IEnumerable of a specified property?

I've written the following code for retrieving the StructureIds from an IEnumerable<Structure>:
Action<Structure> recurse = null;
List<int> structureIds = new List<int>();
recurse = (r) =>
{
structureIds.Add(r.StructureId);
r.Children.ForEach(recurse);
};
IEnumerable<Structure> structures = GetStructures();
structures.ForEach(recurse);
I'd really like to make this generic so I can use it with any IEnumerable, i.e. something like:
public static IEnumerable<TType> GetPropertyValues<TType, TPropertyType>(
this IEnumerable<TType> this, <Property Declaration>)
{
// Generic version of the above code?
}
Can this be done?
Action isn't very Linq'ish. How about Func instead? (Untested code)
public static IEnumerable<TProp> RecurseSelect<TSource, TProp>(
this IEnumerable<TSource> source,
Func<TSource, TProp> propertySelector,
Func<TSource, IEnumerable<TSource>> childrenSelector
)
{
foreach(TSource x in source)
{
yield return propertySelector(x);
IEnumerable<TSource> children = childrenSelector(x);
IEnumerable<TProp> values = children.RecurseSelect(propertySelector, childrenSelector);
foreach(TProp y in values)
{
yield return y;
}
}
}
And then
IEnumerable<Structure> structures = GetStructures();
IEnumerable<int> structureIds = structures.RecurseSelect(
s => s.StructureId,
s => s.Children);
Your problem is that you're not adding each item to a list, you're adding the a property of each item. That property will only be available for a Structure, and not any other type you might reuse the code with.
You also don't have a mechanism for getting the children of your other classes. (the r.Children property you use).
Your two solutions would be to use interfaces (that is, define IHasChildren and IGetProperty) that could be used as base types for a simple algorithm, or you could pass in functions to your method that allow this to be more freely calculated. For example, your method signature might need to be this:
public static IEnumerable<TPropertyType> GetPropertyValues<TType, TPropertyType>
(this IEnumerable<TType> rootItem, Func<TType, IEnumerable<TType>> getChildren, Func<TType, TPropertyType> getIdValue)
... but that's not going to be very pretty!

IEqualityComparer exception

I am using Entity Framework 4.0 and trying to use the "Contains" function of one the object sets in my context object. to do so i coded a Comparer class:
public class RatingInfoComparer : IEqualityComparer<RatingInfo>
{
public bool Equals(RatingInfo x, RatingInfo y)
{
var a = new {x.PlugInID,x.RatingInfoUserIP};
var b = new {y.PlugInID,y.RatingInfoUserIP};
if(a.PlugInID == b.PlugInID && a.RatingInfoUserIP.Equals(b.RatingInfoUserIP))
return true;
else
return false;
}
public int GetHashCode(RatingInfo obj)
{
var a = new { obj.PlugInID, obj.RatingInfoUserIP };
if (Object.ReferenceEquals(obj, null))
return 0;
return a.GetHashCode();
}
}
when i try to use the comparer with this code:
public void SaveRatingInfo2(int plugInId, string userInfo)
{
RatingInfo ri = new RatingInfo()
{
PlugInID = plugInId,
RatingInfoUser = userInfo,
RatingInfoUserIP = "192.168.1.100"
};
//This is where i get the execption
if (!context.RatingInfoes.Contains<RatingInfo>(ri, new RatingInfoComparer()))
{
//my Entity Framework context object
context.RatingInfoes.AddObject(ri);
context.SaveChanges();
}
}
i get an execption:
"LINQ to Entities does not recognize the method 'Boolean Contains[RatingInfo](System.Linq.IQueryable1[OlafCMSLibrary.Models.RatingInfo], OlafCMSLibrary.Models.RatingInfo,
System.Collections.Generic.IEqualityComparer1[OlafCMSLibrary.Models.RatingInfo])' method, and his method cannot be translated into a store expression."
Since i am not proficient with linQ and Entity Framework i might be making a mistake with my use of the "var" either in the "GetHashCode" function or in general.
If my mistake is clear to you do tell me :) it does not stop my project! but it is essential for me to understand why a simple comparer doesnt work.
Thanks
Aaron
LINQ to Entities works by converting an expression tree into queries against an object model through the IQueryable interface. This means than you can only put things into the expression tree which LINQ to Entities understands.
It doesn't understand the Contains method you are using, so it throws the exception you see. Here is a list of methods which it understands.
Under the Set Methods section header, it lists Contains using an item as supported, but it lists Contains with an IEqualityComparer as not supported. This is presumably because it would have to be able to work out how to convert your IEqualityComparer into a query against the object model, which would be difficult. You might be able to do what you want using multiple Where clauses, see which ones are supported further up the document.

Resources