Is there a way to parse all all the classes in a groovy script?
To Parse ONE class right now:
java.lang.Class clazz = groovyClassLoader.parseClass(new File("MainApp.groovy"))
MainApp.groovy:
class MainApp {
def doIt() {}
}
class OtherMainApp {
def doTheRest() {}
}
This will return only MainApp.
I would like something like this:
java.lang.Class[] clazz = groovyClassLoader.parseClass(new File("MainApp.groovy"))
where clazz contains will contain both MainApp class and OtherMainApp class
Basically I want to be able to extract all the declared classes in a script.
Because of the nature of the app that I'm building groovyc command won't help
Thanks,
Federico
No can do:
https://issues.apache.org/jira/browse/GROOVY-3793
You could do it yourself though: you could parse the class yourself (just count the {} pairs), dump it out to a new file, and away you go. Ugly? yes. Painful? Very. Possible? Maybe. Better solution? Not until Groovy fixes the bug.
It's been 12 years since this question was asked and answered, but it arrived at the top of my Google results when I was trying to figure out how to do the same thing.
This ended up working for me:
def loader = new GroovyClassLoader()
loader.parseClass(new File("Company.groovy"))
Class Location = loader.loadClass("tk.vallerance.spacecompanies.models.Location")
Class Event = loader.loadClass("tk.vallerance.spacecompanies.models.Event")
Class Company = loader.loadClass("tk.vallerance.spacecompanies.models.Company")
println Location
println Event
println Company
Related
I'm new to Gradle and Groovy.
I have something like this in my build.gradle file:
jacocoTestReport {
afterEvaluate {
classDirectories.setFrom(files(classDirectories.files.collect {
fileTree(dir: it, exclude: ['aaa/bbb.*', 'ccc/ddd/*',])
}))
}
}
Now, I need this in my custom plugin. But I'm not sure how to set these excludes to it.
I have something like this:
project.afterEvaluate { p ->
def reportTask = project.tasks.findByName('jacocoTestReport') as JacocoReport
reportTask.classDirectories.setFrom(reportTask.classDirectories.files.) // Now what?
}
And I'm stuck here. Can you help me sort this out, please?
EDIT1:
I tried to solve it using patternSet.
println '----------------BEFORE REPORT----------------------'
def patternSet = new PatternSet()
def filesAsString = reportTask.classDirectories.files.collect { file ->
file.path
}
patternSet.setIncludes(filesAsString)
patternSet.setExcludes(['aaa/bbb.*', 'ccc/ddd/*'])
def excludeFiles = reportTask.classDirectories.asFileTree.matching(patternSet).files
println filesAsString
println excludeFiles
def files = reportTask.classDirectories.asFileTree.minus(excludeFiles)
reportTask.classDirectories.setFrom(files)
println '----------------AFTER REPORT----------------------'
println reportTask.classDirectories.files
Result is not what I expected:
----------------BEFORE REPORT----------------------
[aaa\bbb\build\classes\java\main, aaa\bbb\build\classes\groovy\main, aaa\bbb\build\resources\main]
[]
----------------AFTER REPORT----------------------
[]
No exclude directories at all.
So, I somehow solved it.
def ex = ['aaa/bbb/*','ccc/ddd.*','eee/fff/*']
def ft = reportTask.classDirectories.asFileTree.matching {
ex.each {
exclude it
}
}
Don't ask me how Gradle wraps this since it's not a Groovy syntax, but it works.
My opinion is that Gradle somehow implements AntlrTask as a pattern matcher since it implements interface 'PatternFilterable' and documentation mentions Ant. I tried to solve it in pure Groovy using PatternSet which is another class implementing PatternFilterable but I haven't succeded.
But the simple exclude 'aaa/bbb/*' works OK. It doesn't take list so I had to do it using forEach cycle. Maybe there is more simple and elegant way and I'll try to play with it but this solution is good enough.
I am using groovy 2.3.8
I am trying to figure out how method calls work in groovy. Specifically if we have a Java class hierarchy each having a metaClass like below
class A {
}
A.metaClass.hello = {
"hello superclass"
}
class B extends A {
}
B.metaClass.hello = {
"hello subclass"
}
If I use new B().hello() I get hello subclass. If I remove meta class of B then I get hello superclass.
Based on changing the above example I think groovy goes in the below sequence to find which method to call
method-in-subclass's-metaclass ?: subclass-metho ?: method-in-superclass's metaclass ?: method-in-superclass
So how does groovy lookup which method to call?
Well, the hierarchy is the expected object oriented programming method overloading, which is what you witnessed. What differs is the dispatching. Instead of starting with a method lookup in instance's class, it begins with the MOP (meta object protocol).
In layman's terms, because the MOP is programmable, so is the way methods are invoked :)
How it works
The following diagram from Groovy's documentation shows how methods are looked up.
What's not clear in the diagram is that there's an instance metaclass as well, and it comes before the class's metaclass.
Something that may help is looking at an object's or class's .metaClass.methods Methods added through inheritance, traits, metaclass, etc are listed in a flat list. The inheritance hierarchy is flattened. .metaClass.metaMethods on the other hand seems to contain methods added via the GDK. From the list I could not tell method precedence :(
Based on observation, the rule seems to be this: the last MetaClass standing wins.
class A { }
class B extends A { }
A.metaClass.hello = {
"hello superclass"
}
B.metaClass.hello = {
"hello subclass"
}
def b = new B()
assert b.hello() == "hello subclass"
b.metaClass = A.metaClass
assert b.hello() == "hello superclass"
What is the best way to identify, on a NFactory parsed tree, if the current class is actually inheriting (or not) from another class?
you should see if the parsed tree has any basetypes or not.
TypeDeclaration typeDeclaration = new CSharpParser().Parse("public class A:B{}").Children.OfType<TypeDeclaration>().First();
if(typeDeclaration.ClassType==ClassType.Class && typeDeclaration.BaseTypes.Count>=1)
{
//this class is inherited..
}
Can I somehow mixin a class with parameterized constructor?
Something like this:
class RenderedWithTemplates {
def templates = []
RenderedWithTemplates(templates) { ... }
...
}
#Mixin(RenderedWithTemplates(show: "showAddress.gsp", add: "addAddress.gsp")
class Address { ... }
I found the mixin proposal from 2007 [0], but the GroovyDefaultMethods#mixin [1] method has no support for parameterized mixins and neither has #Mixin.
As far as I can tell from the code example above, you need to find a way to mixin GSP view information that is tied to your (domain) classes. An alternative (and slightly groovier ;)) approach for this case would be to implement RenderedWithTemplates as annotation with a single closure parameter holding the GSP view information:
import java.lang.annotation.*
#Retention(RetentionPolicy.RUNTIME)
#interface RenderedWithTemplates {
Class value()
}
#RenderedWithTemplates({ [show: "showAddress.gsp", add: "addAddress.gsp"] })
class Address {}
// shows how to read the map with all the GSP infos
def templateInfo = Address.getAnnotation(RenderedWithTemplates)
def gspMap = templateInfo.value().newInstance(this, this).call()
[0] http://docs.codehaus.org/display/GroovyJSR/Mixins
[1] http://groovy.codehaus.org/api/org/codehaus/groovy/runtime/DefaultGroovyMethods.html
I've built a custom builder in Groovy by extending BuilderSupport. It works well when configured like nearly every builder code sample out there:
def builder = new MyBuilder()
builder.foo {
"Some Entry" (property1:value1, property2: value2)
}
This, of course, works perfectly. The problem is that I don't want the information I'm building to be in the code. I want to have this information in a file somewhere that is read in and built into objects by the builder. I cannot figure out how to do this.
I can't even make this work by moving the simple entry around in the code.
This works:
def textClosure = { "Some Entry" (property1:value1, property2: value2) }
builder.foo(textClosure)
because textClosure is a closure.
If I do this:
def text = '"Some Entry" (property1:value1, property2: value2)'
def textClosure = { text }
builder.foo(textClosure)
the builder only gets called for the "foo" node. I've tried many variants of this, including passing the text block directly into the builder without wrapping it in a closure. They all yield the same result.
Is there some way I take a piece of arbitrary text and pass it into my builder so that it will be able to correctly parse and build it?
Your problem is that a String is not Groovy code. The way ConfigSlurper handles this is to compile the text into an instance of Script using GroovyClassLoader#parseClass. e.g.,
// create a Binding subclass that delegates to the builder
class MyBinding extends Binding {
def builder
Object getVariable(String name) {
return { Object... args -> builder.invokeMethod(name,args) }
}
}
// parse the script and run it against the builder
new File("foo.groovy").withInputStream { input ->
Script s = new GroovyClassLoader().parseClass(input).newInstance()
s.binding = new MyBinding(builder:builder)
s.run()
}
The subclass of Binding simply returns a closure for all variables that delegates the call to the builder. So assuming foo.groovy contains:
foo {
"Some Entry" (property1:value1, property2: value2)
}
It would be equivalent to your code above.
I think the problem you described is better solved with a slurper or parser.
See:
http://groovy.codehaus.org/Reading+XML+using+Groovy%27s+XmlSlurper
http://groovy.codehaus.org/Reading+XML+using+Groovy%27s+XmlParser
for XML based examples.
In your case. Given the XML file:
<foo>
<entry name='Some Entry' property1="value1" property2="value2"/>
</foo>
You could slurp it with:
def text = new File("test.xml").text
def foo = new XmlSlurper().parseText(text)
def allEntries = foo.entry
allEntries.each {
println it.#name
println it.#property1
println it.#property2
}
Originally, I wanted to be able to specify
"Some Entry" (property1:value1, property2: value2)
in an external file. I'm specifically trying to avoid XML and XML-like syntax to make these files easier for regular users to create and modify. My current solution uses ConfigSlurper and the file now looks like:
"Some Entry"
{
property1 = value1
property2 = value2
}
ConfigSlurper gives me a map like this:
["Some Entry":[property1:value1,property2:value2]]
It's pretty simple to use these values to create my objects, especially since I can just pass the property/value map into the constructor.