For DSL purposes I want to detect methods defined like:
def "methodName"() {}
or
def "This is another method name"() {}
Is there any way to do that using reflection?
This information is not available at runtime. Even with an AST transformation, you would not be able to figure out that the method was defined using a string.
For this, you would have to write your own AntlrParserPlugin and use it using a custom CompilerConfiguration. Then in methodDef from AntlrParserPlugin, when the name of the method is parsed, you could check that the character before the name is a double quote. If a double quote is found, then add a custom annotation to the generated MethodNode, so that the information is available at runtime...
Related
I'm building a route which calls a groovy script whose path is dynamically computed and, if the script can't be found, defaults to a generic, static script:
.doTry()
.toD("language://groovy:resource:classpath:scripts/${exchangeProperty.consumerType}ResponseHandler.groovy")
.doCatch(FileNotFoundException.class)
.script().groovy("resource:classpath:scripts/defaultResponseHandler.groovy")
.end()
The problem is that the exchange property consumerType is not resolved since the uri string parameter of toD is evaluated using groovy and not simple.
MultipleCompilationErrorsException -> startup failed:
Script_09b4150584d9e2c979353feee06897b5.groovy: 1: Unexpected input: 'scripts/${exchangeProperty.consumerType}' # line 1, column 20.
resource:classpath:scripts/${exchangeProperty.consumerType}ResponseHandler.groovy
^
1 error
How can I obtain the desired behavior?
According to the error shown there, it seems Camel is not able to resolve the string you provided in the toD().
By default, the expression you pass to a dynamic to is evaluated as Simple language but, as described in To Dynamic Camel documentation, you can specify other languages for the dynamic evaluation.
In your case, you are trying to evaluate the endpoint with groovy language but then you're using Simple language to substitute a piece of the name of the script.
One solution I've found (yet not the best) would be to specify the language for the interpretation of the string as simple and then use language:groovy to specify the endpoint that will need to be called.
You could write something like this:
.doTry()
.toD("language:simple:language://groovy:resource:classpath:scripts/${exchangeProperty.consumerType}ResponseHandler.groovy")
.doCatch(FileNotFoundException.class)
.script().groovy("resource:classpath:scripts/defaultResponseHandler.groovy")
.end()
It seems to work, but I hope someone else comes up with a better solution.
I'm writing a class named "MyObject".
one of the class methods is:
addTo: aCodeString assertType: aTypeCollection
when the method is called with aCodeString, I want to add (in runtime) a new method to "MyObject" class which aCodeString is it's source code and inject type checking code into the source code.
for example, if I call addTo: assertType: like that:
a := MyObject new.
a addTo: 'foo: a boo:b baz: c
^(a*b+c)'
assertType: #(SmallInteger SmallInteger SmallInteger).
I expect that I could write later:
answer := (a foo: 2 boo: 5 baz: 10).
and get 20 in answer.
and if I write:
a foo: 'someString' boo: 5 baz: 10.
I get the proper message because 'someString' is not a SmallInteger.
I know how to write the type checking code, and I know that to add the method to the class in runtime I can use 'compile' method from Behavior class.
the problem is that I want to add the type checking code inside the source code.
I'm not really familiar with all of squeak classes so I'm not sure if I rather edit the aCodeString as a string inside addTo: assertType: and then use compile: (and I don't know how to do so), or that there is a way to inject code to an existing method in Behavior class or other squeak class.
so basically, what I'm asking is how can I inject string into an existing string or to inject code into an existing method.
There are many ways you could achieve such type checking...
The one you propose is to modify the source code (a String) so as to insert additional pre-condition type checks.
The key point with this approach is that you will have to insert the type checking at the right place. That means somehow parsing the original source (or at least the selector and arguments) so as to find its exact span (and the argument names).
See method initPattern:return: in Parser and its senders. You will find quite low level (not most beautiful) code that feed the block (passed thru return: keyword) with sap an Array of 3 objects: the method selector, the method arguments and the method precedence (a code telling if the method is connected to unary, binary or keyword message). From there, you'll get enough material for achieving source code manipulation (insert a string into another with copyReplace:from:to:with:).
Do not hesitate to write small snippets of code and execute in the Debugger (select code to debug, then use debug it menu or ALT+Shift+D). Also use the inspectors extensively to gain more insight on how things work!
Another solution is to parse the whole Abstract Syntax Tree (AST) of the source code, and manipulate that AST to insert the type checks. Normally, the Parser builds the AST, so observe how it works. From the modified AST, you can then generate new CompiledMethod (the bytecode instructions) and install it in methodDictionary - see the source code of compile: and follow the message sent until you discover generateMethodFromNode:trailer:. This is a bit more involved, and has a bad side effect that the source code is now not in phase with generated code, which might become a problem once you want to debug the method (fortunately, Squeak can used decompiled code in place of source code!).
Last, you can also arrange to have an alternate compiler and parser for some of your classes (see compilerClass and/or parserClass). The alternate TypeHintParser would accept modified syntax with the type hints in source code (once upon a time, it was implemented with type hints following the args inside angle brackets foo: x <Integer> bar: y <Number>). And the alternate TypeHintCompiler would arrange to compile preconditions automatically given those type hints. Since you will then be very advanced in Squeak, you will also create special mapping between source code index and bytecodes so as to have sane debugger and even special Decompiler class that could recognize the precondition type checks and transform them back to type hints just in case.
My advice would be to start with the first approach that you are proposing.
EDIT
I forgot to say, there is yet another way, but it is currently available in Pharo rather than Squeak: Pharo compiler (named OpalCompiler) does reify the bytecode instructions as objects (class names beginning with IR) in the generation phase. So it is also possible to directly manipulate the bytecode instructions by proper hacking at this stage... I'm pretty sure that we can find examples of usage. Probably the most advanced technic.
I imagine I'm screwing something up with these declarations, but I've got a groovy class with a field defined like this:
Map<String, SomeType> _someField = [:]
I do inserts like this:
_someField.put( someStringVariable, someTypeInstance )
...and then later, when I check whether a key I know has been mapped is present, the check fails:
_someField.containsKey( someStringVariable )
The only way I can get this to succeed is by calling toString(), like so:
_someField.containsKey( someStringVariable.toString() )
I'm using the generic declaration of the map so my IDE gives me auto completion on the value types, so I'd really like (I think) to keep the type information there.
I've tried changing the key type from String to GString, but to no avail. I've tried changing the map initialization from the groovy shorthand [:] to new LinkedHashMap<>, also to no avail.
Any ideas whether I can keep the type information and avoid having to use toString()?
So this was a case where the variable being fed to containsKey() in the instances where it is failing were of type org.codehaus.groovy.runtime.GStringImpl because they were generated by a function that was performing variable expansion on map values, and that function was creating groovy interpolated strings for values instead of Java Strings.
A quick check on the type of the variable confirmed the type problem, and then it was just a matter of tracking back to find the source of the interpolated string.
I am using a simple looping plugin so that my template looks like this:
{exp:loop_plus start="1" end="4" increment="1"}
<h3>{slide_{index}_title}</h3>
{/exp:loop_plus}
However, I am ending up with the following output:
<h3>{slide_1_title}</h3>
<h3>{slide_2_title}</h3>
<h3>{slide_3_title}</h3>
<h3>{slide_4_title}</h3>
Is there any way I can have dynamic variable names like this? I am not looking for alternative methods for building a slider, I simply would like to know if the dynamic variable names like this is possible. Thanks!
I'm assuming that Loop Plus (http://devot-ee.com/add-ons/loop-plus) sets the {index} part, so the question is what is defining {slide_1_title}...?
Assuming you have an entry field or variable with this defined, what you have is correct, but if it's not working, it means there's a parsing order issue.
Let's assume the code you supplied is wrapped in a {exp:channel:entries} tag pair, what happens is EE will try to parse the variable first, so will see: {slide_{index}_title} which doesn't exist. The {exp:loop_plus} add-on will then parse it, converting it to {slide_1_title} (but to late as channel:entries has already tried to parse it), which is what is finally output to the template.
So what you want to ensure is that EE parses {exp:loop_plus} before {exp:channel:entries}, do this using parse="inward" tag:
{exp:loop_plus start="1" end="4" increment="1" parse="inward"}
<h3>{slide_{index}_title}</h3>
{/exp:loop_plus}
This is a global EE parameter that EE uses to control parse order - you won't find it documented under the specific add-on. By adding the parameter, it means this child tag will get parsed before it's parent.
One way you could do it is to declare a preload_replace variable in your template and use it in your custom field name.
So something like:
{preload_replace:my_var_prefix="whatever"}
And then in your loop, you could then use:
{slide_{my_var_prefix}_title}
As of scala 2.10, the following interpolation is possible.
val name = "someName"
val interpolated = s"Hello world, my name is $name"
Now it is also possible defining custom string interpolations, as you can see in the scala documentation in the "Advanced usage" section here http://docs.scala-lang.org/overviews/core/string-interpolation.html#advanced_usage
Now then, my question is... is there a way to obtain the original string, before interpolation, including any interpolated variable names, from inside the implicit class that is defining the new interpolation for strings?
In other words, i want to be able to define an interpolation x, in such a way that when i call
x"My interpolated string has a $name"
i can obtain the string exactly as seen above, without replacing the $name part, inside the interpolation.
Edit: on a quick note, the reason i want to do this is because i want to obtain the original string and replace it with another string, an internationalized string, and then replace the variable values. This is the main reason i want to get the original string with no interpolation performed on it.
Thanks in advance.
Since Scala's string interpolation can handle arbitrary expressions within ${} it has to evaluate the arguments before passing them to the formatting function. Thus, direct access to the variable names is not possible by design. As pointed out by Eugene, it is possible to get the name of a plain variable by using macros. I don't think this is a very scalable solution, though. After all, you'll lose the possibility to evaluate arbitrary expressions. What, for instance, will happen in this case:
x"My interpolated string has a ${"Mr. " + name}"
You might be able to extract the variable name by using macros but it might get complicated for arbitrary expressions. My suggestions would be: If the name of your variable should be meaningful within the string interpolation, make it a part of the data structure. For example, you can do the following:
case class NamedValue(variableName: String, value: Any)
val name = NamedValue("name", "Some Name")
x"My interpolated string has a $name"
The objects are passed as Any* to the x. Thus, you now can match for NamedValue within x and you can do specific things depending on the "variable name", which now is part of your data structure. Instead of storing the variable name explicitly you could also exploit a type hierarchy, for instance:
sealed trait InterpolationType
case class InterpolationTypeName(name: String) extends InterpolationType
case class InterpolationTypeDate(date: String) extends InterpolationType
val name = InterpolationTypeName("Someone")
val date = InterpolationTypeDate("2013-02-13")
x"$name is born on $date"
Again, within x you can match for the InterpolationType subtype and handle things according to the type.
It seems that's not possible. String interpolation seems like a compile feature that compiles the example to:
StringContext("My interpolated string has a ").x(name)
As you can see the $name part is already gone. It became really clear for me when I looked at the source code of StringContext: https://github.com/scala/scala/blob/v2.10.0/src/library/scala/StringContext.scala#L1
If you define x as a macro, then you will be able to see the tree of the desugaring produced by the compiler (as shown by #EECOLOR). In that tree, the "name" argument will be seen as Ident(newTermName("name")), so you'll be able to extract a name from there. Be sure to take a look at macro and reflection guides at docs.scala-lang.org to learn how to write macros and work with trees.