I am trying to dynamically configure the createIndex flag:
#Document(indexName = "#{#myindex}", createIndex = "#{#shouldIndex}")
public class MyDocument {
...
}
This throws an error because createIndex expects boolean. Is there any way to return boolean type from el expression?
It's not a question of SpEL returning boolean; the property is hard-wired as a boolean...
boolean createIndex() default true;
So it can't be determined at runtime; it won't accept a SpEL expression, which is a String.
Related
I would like to convert a parameter and then call second method with this parameter.
The convention would be that there is always the same method overloaded with the specific type. The idea is to solve it with Spring AOP.
#Component
public class ExampleAspect {
#Around( "#annotation(Example)" )
public Object test( final ProceedingJoinPoint joinPoint ) throws Throwable {
final MethodSignature signature = (MethodSignature) joinPoint.getSignature();
final Method method = signature.getMethod();
final Example example = method.getAnnotation( Example.class );
final Object[] args = joinPoint.getArgs();
final String test = args[example.value()].toString();
final Bar bar = convertToBar(test)
args[example.value()] = bar;
//ReflectionUtils?
// call getBar(Bar bar)
//return joinPoint.proceed( args );
}
}
Here is the service
#Example(0)
public Object getBar(String test) {}
public Object getBar(Bar test) {}
Are there any better options or ideas?
EDIT:
Cannot inject the target bean, because this AOP should be used by more than specific target bean.
1 possible solution not sure if there is a smarter solution
#Around("#annotation(Example)")
public Object test(final ProceedingJoinPoint joinPoint) throws Throwable {
final MethodSignature signature = (MethodSignature) joinPoint.getSignature();
final Method method = signature.getMethod();
final Example example = method.getAnnotation(Example.class);
final Object[] args = joinPoint.getArgs();
final String bar = args[example.value()].toString();
final Bar aspectModelUrn = convertFromStringToBar(bar);
args[example.value()] = bar;
final Class<?>[] parameterTypes = method.getParameterTypes();
parameterTypes[example.value()] = Bar.class;
final Method newMethod = ReflectionUtils.findMethod(joinPoint.getTarget().getClass(), method.getName(), parameterTypes);
if (newMethod == null) {
throw new IllegalArgumentException("There is no method blubb. Have you forget to create the delegate method");
}
return newMethod.invoke(joinPoint.getTarget(), args);
}
Following code would provide a handle to the annotation and the target bean (for example , here TestComponent)
A call to the TestComponent.getBar() annotated with #Example would be intercepted and advised.
#Aspect
#Component
public class ExampleAspect {
#Around("#annotation(example) && target(bean)")
public Object test(final ProceedingJoinPoint joinPoint,Example example,TestComponent bean) throws Throwable {
String value = String.valueOf(example.value());
Bar bar = convertToBar(value);
bean.getBar(bar);
return joinPoint.proceed();
}
}
Do go through Spring AOP documentation : Passing Parameters to Advice for more details.
Note : For better performance it is a good idea to limit the scope of the expression as follows.
#Around("#annotation(example) && within(com.xyz.service..*) && target(bean)")
where com.xyz.service..* will limit the expression scope only to the beans with in the package com.xyz.service..* and its sub-packages.
I want to add a BigDecimal to a list using Spring Expression Language.
public class SpelTest {
public List<BigDecimal> values;
StandardEvaluationContext context;
SpelExpressionParser parser;
#Before
public void setup() {
values = new ArrayList<>();
context = new StandardEvaluationContext(this);
parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
}
#Test
public void shouldChangeValue() {
values.add(BigDecimal.ONE);
parser.parseExpression("values[0]").setValue(context, "123.4");
assertThat(values.get(0)).isEqualTo(BigDecimal.valueOf(123.4)); // passes
}
#Test
public void shouldAddValue() {
parser.parseExpression("values[0]").setValue(context, "123.4");
assertThat(values.get(0)).isEqualTo(BigDecimal.valueOf(123.4)); // fails
}
}
Changing the first entry passes but adding an entry fails with
Caused by: java.lang.NoSuchMethodException: java.math.BigDecimal.<init>()
at java.base/java.lang.Class.getConstructor0(Class.java:3349)
at java.base/java.lang.Class.getDeclaredConstructor(Class.java:2553)
at org.springframework.util.ReflectionUtils.accessibleConstructor(ReflectionUtils.java:185)
at org.springframework.expression.spel.ast.Indexer$CollectionIndexingValueRef.growCollectionIfNecessary(Indexer.java:715)
... 55 more
Not sure why SpEL isn't able to properly initialize a BigDecimal when the list is empty. Surprisingly I found nothing about this problem.
Thanks for helping!
The problem is that you activated autoGrowCollections on the SpelParserConfiguraion.
Therefore it tries to create an element with a default constructor if you try to access a non-existing element of the collection with the index operator []. BigDecimal has no default constructor and because of this, it fails.
What you could do is to create the object in the SpEL itself. E.G.:
#Test
public void shouldAddValue() {
parser.parseExpression("values.add(0, new java.math.BigDecimal(\"123.4\"))").getValue(context);
assertThat(values.size() > 0);
assertThat(values.get(0)).isEqualTo(BigDecimal.valueOf(123.4));
}
Or you could create a subclass of BigDecimal witch has a default constructor and use this class.
You could avoid this problem by setting the whole list instead of single (not initialized) elements. Instead of
parser.parseExpression("values[0]").setValue(context, "123.4");
use:
parser.parseExpression("values").setValue(context, "123.4");
This also works for multiple elements, quite neat:
parser.parseExpression("values").setValue(context, "123.4, 456.7");
As #Nirud pointed out, the problem is, that BigDecimal does not have a default constructor. I extended SpEL to add null to the list, when there is no default constructor. See this pull request: https://github.com/spring-projects/spring-framework/pull/25367
I'm trying to implement a Gtk.StyleProvider in Vala. The "base class" (in C) looks like:
GtkIconFactory * gtk_style_provider_get_icon_factory ()
GtkStyleProperties * gtk_style_provider_get_style ()
gboolean gtk_style_provider_get_style_property ()
and in VAPI:
[CCode (cheader_filename = "gtk/gtk.h")]
public interface StyleProvider {
public abstract unowned Gtk.IconFactory get_icon_factory (Gtk.WidgetPath path);
public abstract unowned Gtk.StyleProperties get_style (Gtk.WidgetPath path);
public abstract bool get_style_property (Gtk.WidgetPath path, Gtk.StateFlags state, GLib.ParamSpec pspec, GLib.Value value);
}
Where the first two methods should only return NULL according to the documentation for GtkStyleProvider.
Thus, I wrote some Vala like this:
public class DerivedStyleProvider : Gtk.StyleProvider
{
public Gtk.IconFactory? get_icon_factory (Gtk.WidgetPath path)
{
return null;
}
public Gtk.StyleProperties? get_style (Gtk.WidgetPath path)
{
return null;
}
bool get_style_property (Gtk.WidgetPath path,
Gtk.StateFlags state,
GLib.ParamSpec pspec,
out GLib.Value value)
{
return false; //TODO
}
}
I have a problem with the first two methods. If I have them as written here (with a ?), then I get the following error:
error: overriding method `DerivedStyleProvider.get_icon_factory' is incompatible
with base method `Gtk.StyleProvider.get_icon_factory': Base method expected
return type `Gtk.IconFactory', but `Gtk.IconFactory?' was provided.
public Gtk.IconFactory? get_icon_factory (Gtk.WidgetPath path)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The gtk_style_provider_get_style() method is the same.
If I remove the ?, I get the following two errors per method:
error: overriding method `DerivedsStyleProvider.get_icon_factory'
is incompatible with base method `Gtk.StyleProvider.get_icon_factory': Base
method expected return type `Gtk.IconFactory', but `Gtk.IconFactory' was provided.
public Gtk.IconFactory get_icon_factory (Gtk.WidgetPath path)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/Preferences.vala:138.3-138.14: warning: `null' incompatible with
return type `Gtk.IconFactory`
return null;
^^^^^^^^^^^
The first error especially is a bit strange to me, as the upshot is "error: expected TYPE, got TYPE"!
Adding unowned to the first two methods still results in similar errors.
How should I implement a Gtk.StyleProvider interface in Vala?
This compiles without errors or warnings on my system (Vala 0.32.1):
public class DerivedStyleProvider : GLib.Object, Gtk.StyleProvider
{
public unowned Gtk.IconFactory get_icon_factory (Gtk.WidgetPath path)
{
// Evil cast to work around buggy declaration in VAPI file
return (Gtk.IconFactory) null;
}
public Gtk.StyleProperties get_style (Gtk.WidgetPath path)
{
// Evil cast to work around buggy declaration in VAPI file
return (Gtk.StyleProperties) null;
}
bool get_style_property (Gtk.WidgetPath path,
Gtk.StateFlags state,
GLib.ParamSpec pspec,
out GLib.Value value)
{
// I just assigned something here to make the compiler happy, you should make sure to use a correct value
value = Value (typeof (string));
return false; //TODO
}
}
I made these changes:
Derive from GLib.Object in addition to the interface.
Use unowned on the first method.
Remove the nullable from the return types.
Cast null into the actual class types. (Which is not pretty, but the problem is with the vapi file.)
Assign a dummy value to the out parameter to make compiling warning free ;)
Using FitNesse with FitSharp (.Net), I've got a property of an object that is a HashSet of an enum, e.g.
public enum Fubar { Foo, Bar }
public class Test
{
public HashSet<Fubar> SetOfFubar { get; set; }
}
I'd like to test it simply, for example in an array fixture like so:
|MyArrayFixture|
|SetOfFubar|
|Foo|
|Foo,Bar|
|Bar|
||
This simply results in all rows marked missing & red and a set of surplus rows showing something like:
System.Collections.Generic.HashSet`1[MyNamespace.Fubar] surplus
What's the easiest way to get FitNesse with FitSharp (.Net) to understand HashSets?
You can write a parse operator. Here's a little example from http://fitsharp.github.io/Fit/WriteOurOwnCellOperator.html that parses the string"pi" into a numeric value.
using fitSharp.Fit.Operators;
using fitSharp.Machine.Engine;
public class ParsePi: CellOperator, ParseOperator<Cell> {
The CanParse method override determines which cells are processed by
our operator. We check for "pi" in the cell and also that we are
working with a numeric type. This preserves normal behavior if we're
using "pi" as a string input or expected value.
public bool CanParse(Type type, TypedValue instance, Tree<Cell> parameters) {
return type == typeof(double) && parameters.Value.Text == "pi";
}
Overriding the Parse method changes the behavior when the cell is
parsed to create an input or an expected value.
public TypedValue Parse(Type type, TypedValue instance, TreeTree<Cell> parameters) {
return new TypedValue(System.Math.PI);
}
}
The ITypeConverter interface has been changed to have a "TDestination Convert(ResolutionContext context)" instead of "TDestination Convert(TSource source)" for the Convert method.
http://automapper.codeplex.com/wikipage?title=Custom%20Type%20Converters
In my code, now I get this error:
'BusinessFacade.Mappers.DecimalToNullableInt' does not implement
interface member
'AutoMapper.ITypeConverter.Convert(AutoMapper.ResolutionContext)'
Any good full sample for new mapper like my mappers ? I don't want change any code (or minimum code) in my projects...
My mapper
public class DecimalToNullableInt : ITypeConverter<decimal, int?>
{
public int? Convert(decimal source)
{
if (source == 0)
return null;
return (int)source;
}
}
UPDATE
The ITypeConverter interface has been changed to have a "TDestination Convert(ResolutionContext context)" instead of "TDestination Convert(TSource source)" for the Convert method.
the documentation is just out of date. There is an ITypeConverter, as
well as a base TypeConverter convenience class. The TypeConverter hides the
ResolutionContext, while ITypeConverter exposes it.
http://automapper.codeplex.com/wikipage?title=Custom%20Type%20Converters
https://github.com/AutoMapper/AutoMapper/wiki/Custom-type-converters
http://groups.google.com/group/automapper-users/browse_thread/thread/6c523b95932f4747
You'll have to grab the decimal from the ResolutionContext.SourceValue property:
public int? Convert(ResolutionContext context)
{
var d = (decimal)context.SourceValue;
if (d == 0)
{
return null;
}
return (int) d;
}