Xtext, import my own mydsl file - java

In my grammar i have an include rule as follow :
Script:
includes+=(Include)* assignments+=(Assignment)* clock=Clock? tests+=Test*
;
Include:
'INCLUDE' importURI=STRING
;
what I want to do is to include files same as the "main" file.
I'm working with an interpreter that handels the .mydsl file.
/* Main exec methode */
def dispatch void exec(Script s) {
s.includes.forEach[ i | i.exec]
s.assignments.forEach[a | a.exec]
s.clock.exec
s.tests.forEach[t|t.exec]
}
/* include methode */
def dispatch void exec(Include i) {
System.out.println( i.importURI + " included")
}

Xtext imports are not includes. Xtext does not support includes at all. All that Xtext supports are Cross References. You can use namespace based or import uri based global scoping to determine how elements from other files can be found. assume you really want to follow the includes files in your interpreter
Script:
includes+=(Include)*
;
Include:
'INCLUDE' includedScript=[Script|STRING]
;
And Name Provider
public class MyDslQNP extends DefaultDeclarativeQualifiedNameProvider {
QualifiedName qualifiedName(Script script) {
return QualifiedName.create(script.eResource().getURI().trimFileExtension().lastSegment(), script.eResource().getURI().fileExtension());
}
}
then you can follow the reference in your interpreter
class MyDslRuntimeModule extends AbstractMyDslRuntimeModule {
override bindIQualifiedNameProvider() {
MyDslQNP
}
}

Related

How to invoke a method of a groovy class from Java - Method name and parameter in string format

I have a Java program which accepts some string input in the below format:
setData("hello")
Also, I have a groovy script say "sample.groovy", it is a groovy file in the following sample format:
class sample
{
def doOperation()
{
println("Inside doOperation()")
}
def setData(String str)
{
println("Incoming data : " + str)
}
}
From the Java class, create an object of above groovy class named : sampleObj.
I have to invoke sampleObj.setData("hello") from my Java application using the input string say "setData("hello")".
Then how can I invoke this method?
This is exactly the kind of problem that GroovyShell solves.
Here's an example:
import groovy.transform.Canonical
import org.codehaus.groovy.control.CompilerConfiguration
#Canonical
class ScriptState {
String data
}
abstract class MyScript extends Script {
void setData(String data) {
binding.state.data = data
}
}
def state = new ScriptState()
def cc = new CompilerConfiguration(scriptBaseClass: MyScript.class.name)
def shell = new GroovyShell(MyScript.classLoader, new Binding(state: state), cc)
shell.evaluate('println "Running script"; setData "The Data"')
assert state.data == 'The Data'
println state
Running this will print:
Running script
ScriptState(The Data)
I based this example on the Groovy Goodness example.
Normally, you don't need to set the classloader as I did in MyScript.classLoader... I only needed to do this because I ran this as a script, to the script class would not be visible to the GroovyShell's script classloader if I didn't do that.
EDIT
After the question was heavily edited, it seem the problem is that you don't know which class the Java object to call from the script will have.
In that case, just change the MyScript class to do something like this:
abstract class MyScript extends Script {
def methodMissing(String name, args) {
// this will call any method called inside the script
// on the sample Object
binding.sampleObject."$name"(*args)
}
}
Now, when creating the GroovyShell:
def shell = new GroovyShell(
MyScript.classLoader,
new Binding(sampleObject: new Sample()),
cc)
Running this code:
shell.evaluate('doOperation(); setData "The Data"')
will print the expected:
Inside doOperation()
Incoming data : The Data

Jeta: How to create custom annotation processors

There is plenty of features that already available on Jeta, but what if something is missing. Can I create my own annotations and generate metacode for them?
Needed a step-by-step tutorial how to create custom Jeta processors.
How to create custom processors, step-by-step tutorial
Step 1: Hello, World project
For this tutorial let's create a simple Gradle project with one module app and with a single class SayHelloApp. This class writes Hello, World! to standard output.
For the illustration we are going to create Hello annotation that sets Hello, Jeta! string to the annotated fields.
Step 2: common module
First, we need a module that will be accessible in app and apt (will create shortly) modules. In common module we need two classes - Hello annotation and HelloMetacode interface:
Step 3: apt module
apt - is a module in which we'll create all the required for code generation classes. For this tutorial we need a processor that will handle our Hello annotation.
Note that this module depends on common module so we used Hello annotation as a parameter for the super constructor. By doing that we're saying to Jeta that we need all the elements annotated with given type. The module also depends on jeta-apt in order to get access to the Jeta classes.
Step 4: Processor
Created SayHelloProcessor now does nothing. Let's add some logic in it. The idea here is to generate java code that sets Hello, Jeta string to the fields annotated with Hello.
Note that Jeta uses JavaPoet to create java source code. It's really great framework by Square. Please, check it out on GitHub.
First, we need that our metacode implements HelloMetacode. To do that we'll add super interface to the builder:
MetacodeContext context = roundContext.metacodeContext();
ClassName masterClassName = ClassName.get(context.masterElement());
builder.addSuperinterface(ParameterizedTypeName.get(
ClassName.get(HelloMetacode.class), masterClassName));
Next, implement HelloMetacode by creating void setHello(M master) method:
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("setHello")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.returns(void.class)
.addParameter(masterClassName, "master");
Finally, the statements for each element annotated with Hello, that Jeta passes in process method via roundContext parameter:
for (Element element : roundContext.elements()) {
String fieldName = element.getSimpleName().toString();
methodBuilder.addStatement("master.$L = \"Hello, Jeta\"", fieldName);
}
Here is the complete SayHelloProcessor listing:
package org.brooth.jeta.samples.apt;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import org.brooth.jeta.apt.MetacodeContext;
import org.brooth.jeta.apt.RoundContext;
import org.brooth.jeta.apt.processors.AbstractProcessor;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
public class SayHelloProcessor extends AbstractProcessor {
public SayHelloProcessor() {
super(Hello.class);
}
#Override
public boolean process(TypeSpec.Builder builder, RoundContext roundContext) {
MetacodeContext context = roundContext.metacodeContext();
ClassName masterClassName = ClassName.get(context.masterElement());
builder.addSuperinterface(ParameterizedTypeName.get(
ClassName.get(HelloMetacode.class), masterClassName));
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("setHello")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.returns(void.class)
.addParameter(masterClassName, "master");
for (Element element : roundContext.elements()) {
String fieldName = element.getSimpleName().toString();
methodBuilder.addStatement("master.$L = \"Hello, Jeta\"", fieldName);
}
builder.addMethod(methodBuilder.build());
return false;
}
}
Step 5: Metacode
All the required for code generating classes are created and we're ready to try. But first, we need to add jeta.properties file in order to configurate Jeta. You can find more details about this file on this page. The file should be located in the root package. For our tutorial its content would be:
metasitory.package=org.brooth.jeta.samples
processors.add=org.brooth.jeta.samples.apt.SayHelloProcessor
Next, modify SayHelloApp. Instead of initializing text field we'll put Hello annotation on it:
public class SayHelloApp {
#Hello
String text;
}
And build.gradle:
group 'org.brooth.jeta-samples'
version '1.0'
buildscript {
repositories {
maven {
url 'https://plugins.gradle.org/m2/'
}
}
dependencies {
classpath 'net.ltgt.gradle:gradle-apt-plugin:0.5'
}
}
apply plugin: 'net.ltgt.apt'
apply plugin: 'java'
sourceCompatibility = 1.7
repositories {
mavenCentral()
jcenter()
}
compileJava {
options.sourcepath = files('src/main/java')
}
dependencies {
apt project(':apt')
compile project(':common')
compile 'org.brooth.jeta:jeta:+'
}
Now we're ready to generate metacode. Run next command in your console:
./gradlew assemble
If there is no problems so far, we'll see SayHelloApp_Metacode file under app/build directory:
Step 6: Controller
Controllers are the classes that apply metacode to the masters. Let's create one for HelloMetacode in app module:
package org.brooth.jeta.samples;
import org.brooth.jeta.MasterController;
import org.brooth.jeta.metasitory.Metasitory;
public class SayHelloController<M> extends MasterController<M, HelloMetacode<M>> {
public SayHelloController(Metasitory metasitory, M master) {
super(metasitory, master, Hello.class, false);
}
public void setHello() {
for (HelloMetacode<M> metacode : metacodes)
metacode.setHello(master);
}
}
Step 7: MetaHelper
MetaHelper is a simple static-helper class. You shouldn't use it in your project if you are not comfortable with static helpers. You can read more details about this class on this page.
Anyway, let's create MetaHelper in app module:
package org.brooth.jeta.samples;
import org.brooth.jeta.metasitory.MapMetasitory;
import org.brooth.jeta.metasitory.Metasitory;
public class MetaHelper {
private static MetaHelper instance;
private final Metasitory metasitory;
public static MetaHelper getInstance() {
if (instance == null)
instance = new MetaHelper("org.brooth.jeta.samples");
return instance;
}
private MetaHelper(String metaPackage) {
metasitory = new MapMetasitory(metaPackage);
}
public static void setHello(Object master) {
new SayHelloController<>(getInstance().metasitory, master).setHello();
}
}
Note that we must pass to MapMetasitory the same package ("org.brooth.jeta.samples") that we specified as metasitory.package in jeta.properties.
Step 8: Usage
The last step - we invoke our MetaHelper's method. Here is the complete listing of SayHelloApp:
package org.brooth.jeta.samples;
public class SayHelloApp {
#Hello
String text;
public SayHelloApp() {
MetaHelper.setHello(this);
}
public void sayHello() {
System.out.print(text);
}
public static void main(String[] args) {
new SayHelloApp().sayHello();
}
}
Finally, we can run SayHelloApp. In the console we should see:
Hello, Jeta
Links
This tutorial on GitHub
Jeta Website
Jeta on Android
Happy code-generating! :)

Avoiding sharing Java meta classes across different Groovy scripts

My situation
I call multiple Groovy scripts from Java, they both contain long-lived Groovy objects.
I would like my Groovy scripts to make some changes to a Java meta-class for a Java class (that have about 100 instances). However, the scripts should be able to make different changes, and changes in one of the scripts should not be reflected in the other scripts.
The problem: The meta-class for the Java class is shared across all the scripts.
This question is similar to How do I undo meta class changes after executing GroovyShell? but in this case I want two scripts to execute simultaneously, so it is not possible to reset after script execution.
Example Code
SameTest.java
public interface SameTest {
void print();
void addMyMeta(String name);
void addJavaMeta(String name);
void callMyMeta(String name);
void callJavaMeta(String name);
}
SameSame.java
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;
public class SameSame {
public SameTest launchNew() {
try {
GroovyScriptEngine scriptEngine = new GroovyScriptEngine(new String[]{""});
Binding binding = new Binding();
binding.setVariable("objJava", this);
SameTest script = (SameTest) scriptEngine.run("test.groovy", binding);
return script;
} catch (Exception | AssertionError e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
SameSame obj = new SameSame();
SameTest a = obj.launchNew();
SameTest b = obj.launchNew();
a.addMyMeta("a");
a.callMyMeta("a");
try {
b.callMyMeta("a");
throw new AssertionError("Should never happen");
} catch (Exception ex) {
System.out.println("Exception caught: " + ex);
}
a.addJavaMeta("q");
b.callJavaMeta("q");
a.print();
b.print();
}
}
test.groovy
ExpandoMetaClass.enableGlobally()
class Test implements SameTest {
SameSame objJava
void print() {
println 'My meta class is ' + Test.metaClass
println 'Java meta is ' + SameSame.metaClass
}
void addMyMeta(String name) {
println "Adding to Groovy: $this $name"
this.metaClass."$name" << {
"$name works!"
}
}
void addJavaMeta(String name) {
println "Adding to Java: $this $name"
objJava.metaClass."$name" << {
"$name works!"
}
}
void callMyMeta(String name) {
println "Calling Groovy: $this $name..."
"$name"()
println "Calling Groovy: $this $name...DONE!"
}
void callJavaMeta(String name) {
println "Calling Java: $this $name..."
objJava."$name"()
println "Calling Java: $this $name...DONE!"
}
}
new Test(objJava: objJava)
Output
Adding to Groovy: Test#7ee955a8 a
Calling Groovy: Test#7ee955a8 a...
Calling Groovy: Test#7ee955a8 a...DONE!
Calling Groovy: Test#4a22f9e2 a...
Exception caught: groovy.lang.MissingMethodException: No signature of method: Test.a() is applicable for argument types: () values: []
Possible solutions: any(), any(groovy.lang.Closure), is(java.lang.Object), wait(), wait(long), each(groovy.lang.Closure)
Adding to Java: Test#7ee955a8 q
Calling Java: Test#4a22f9e2 q...
Calling Java: Test#4a22f9e2 q...DONE!
My meta class is groovy.lang.ExpandoMetaClass#2145b572[class Test]
Java meta is groovy.lang.ExpandoMetaClass#39529185[class SameSame]
My meta class is groovy.lang.ExpandoMetaClass#72f926e6[class Test]
Java meta is groovy.lang.ExpandoMetaClass#39529185[class SameSame]
Desired result
The two lines showing information about the Java meta should be different.
This should crash:
a.addJavaMeta("q");
b.callJavaMeta("q");
The question
Is it possible somehow to use different MetaClassRegistry's in the different GroovyScriptEngine instances?
Or is there any other way to make the desired result as shown above happen?
The feature you are looking for is one I had planed for Groovy 3. But since I will no longer be able to work full time on Groovy and since nobody else dares a big change to the MOP this is no option at the moment.
So is it possible to use different MetaClassRegistry's in the different GroovyScriptEngine instances?
No, since you cannot use different MetaClassRegistry's. The implementation is somewhat abstracted, but the usage of MetaClassRegistryImpl is hardcoded and allows for only one global version.
Or is there any other way to make the desired result as shown above happen?
That depends on your requirements.
If you could let the scripts not share the Java classes (load them using differing class loaders), then you don't have a problem with shared meta classes to begin with (for those). If you want more the idea bayou.io had might be best.
You could provide your own meta class creation handle (see setMetaClassCreationHandle in MetaClassRegistry). Then you would have to of course capture a call like ExpandoMetaClass.enableGlobally(). You could use ExpandoMetaClass with a custom invoker (set someClass.metaClass.invokeMethod = ...) or of course directly extend the class. You would then somehow need a way to recognize that you are coming from one script or the other (there is something called origin or caller in the bigger invokemethod signature, but the information is not always reliable. Same thing for get/setProperty). As for how to reliably and efficiently transport that information... well.. that's something I have no answer for. You have to experiment if what ExpandoMetaClass provides is good enough for you. Maybe you could use a ThreadLocal to store the information... though then you would have to write a transform, which will rewrite all method and property calls and most probably cause a performance disaster.

Java Swing - How to double click a project file on Mac to open my application and load the file?

I have created a Mac Java Swing application, and i have set a file extension(*.pkkt) for it in the "Info.plist" file, so when double clicking that file it opens my application.
When i do that the program runs fine. Now i need to load the (*.pkkt) project in the program, but the file path is not passed as an argument to the main(...) method in Mac as happens in Windows Operating System.
After some search i found an Apple handling jar "MRJToolkitStubs" that has the MRJOpenDocumentHandler interface to handle such clicked files. I have tried using it to load that file by implementing that Interface in the main program class, but it is not working. The implemented method is never called at the program start-up.
How does this Interface run ?
------------------------------------------------- Edit: Add a Code Sample
Here is the code i am using :
public static void main( final String[] args ) {
.
.
.
MacOpenHandler macOpenHandler = new MacOpenHandler();
String projectFilePath = macOpenHandler.getProjectFilePath(); // Always Empty !!
}
class MacOpenHandler implements MRJOpenDocumentHandler {
private String projectFilePath = "";
public MacOpenHandler () {
com.apple.mrj.MRJApplicationUtils.registerOpenDocumentHandler(this) ;
}
#Override
public void handleOpenFile( File projectFile ) {
try {
if( projectFile != null ) {
projectFilePath = projectFile.getCanonicalPath();
System.out.println( projectFilePath ); // Prints the path fine.
}
} catch (IOException e) {}
}
public String getProjectFilePath() {
return projectFilePath;
}
}
As mentioned in the comment above "getProjectFilePath()" is always Empty !
On Java 9, use Desktop.setOpenFileHandler()
The proprietary com.apple.eawt packages have been removed from recent versions of Java and has been incorporated into various methods in the Desktop class. For your specific example:
import java.awt.desktop.OpenFilesHandler;
import java.awt.desktop.OpenFilesEvent;
import java.io.File;
import java.util.List;
public class MyOpenFileHandler implements OpenFilesHandler {
#Override
public void openFiles​(OpenFilesEvent e) {
for (File file: e.getFiles​()) {
// Do whatever
}
}
}
Then elsewhere, add this:
Desktop.getDesktop().setOpenFileHandler(new MyOpenFileHandler());
The OpenFilesEvent class also has a getSearchTerm() method. Say that a person used Spotlight on macOS to search for the word "StackOverflow", then decided to open up a document. With this method, can you determine that "StackOverflow" was the word they searched for, and choose to do something with that (perhaps highlight the first occurrence of the word).
You're going to want to use the Apple Java Extensions.
They should be included in any JDK that runs on Mac OS X, but the documentation is kind of hard to get. See this answer for more details.
Specifically, you'll want to make an OpenFilesHandeler.
This code snippet should work:
import com.apple.eawt.event.OpenFilesHandeler;
import com.apple.eawt.event.AppEvent;
import java.io.File;
import java.util.List;
class MacOpenHandler implements OpenFilesHandeler {
#Override
public void openFiles(AppEvent.OpenFilesEvent e) {
List<File> files = e.getFiles();
// do something
}
}
And somewhere:
import com.apple.eawt.Application;
...
MacOpenHandeler myOpenHandeler = new MacOpenHandeler();
Application.getApplication().setOpenFileHandler(myOpenHandeler);

"Hello World" with Java annotations

Problem description: Compile 2 jar files independently, without having to include each other on classpath; and at runtime, include both and invoke main() in one jar to print a string on stdout. Parts of the output string have to come from the 2nd jar.
Constraints: The solution cannot be IDE-dependent or use any other jar files.
Answering what have I done so far: My Solution is described below.
Reason to ask the question: I am trying to figure out if/how to use annotations to solve this problem (hopefully in a more elegant manner), but cannot find any suitable documentation or tutorial. I appreciate any pointer(s) for that also.
My Solution: A batch file (on Unix, please change the backslash to forward slash, semicolon to colon and the rem's to #) as follows:
rem Compile and package the testing class (Main) without any library
javac -d target core\*.java
cd target
jar cvf ..\main.jar .\core
cd ..
rem Compile and package the Greeting and Greeted classes as a library
javac -d lib impl\*.java
cd lib
jar cvf ..\mylib.jar .\impl
cd ..
rem Use the two parts above at runtime and execute main() in Main class
java -cp main.jar;mylib.jar core.Main
There are 2 files in the impl directory and 2 in the core, as follows:
/* File: impl/Greeting.java */
package impl;
public class Greeting {
public String getGreeting () {
return "Hello";
}}
/* File: impl/Greeted.java */
package impl;
public class Greeted {
public String getGreeted () {
return "world";
}
/* File: core/Main.java */
package core;
public class Main {
private String greeting = "Learn annotations", greeted = "keep using Java",
// Can read the following 4 values from a configuration file, too
// Trying to see if we can get these using Java annotations
greetingClassName = "impl.Greeting", greetingMethod = "getGreeting",
greetedClassName = "impl.Greeted", greetedMethod = "getGreeted";
public Main () {
try {
MyRunTime runTime = new MyRunTime();
Object gting = runTime.getInstance(greetingClassName),
gted = runTime.getInstance(greetedClassName),
g1Str = runTime.getResponseNoArg (gting, greetingMethod),
g2Str = runTime.getResponseNoArg (gted, greetedMethod);
if (g1Str instanceof String) greeting = (String) g1Str;
if (g2Str instanceof String) greeted = (String) g2Str;
} catch (Exception ex) {
System.err.println ("Error in Library loading: " + ex.getMessage());
}}
public void greet () {
System.out.println (greeting + ", " + greeted + "!");
}
public static void main (String[] args) {
new Main().greet();
}}
/* File: core/MyRunTime.java */
package core;
import java.lang.reflect.*;
public class MyRunTime {
public Object getResponseNoArg (Object anInstance, String methodName) throws
NoSuchMethodException,
IllegalAccessException,
InvocationTargetException {
Method method = anInstance.getClass().getMethod (methodName);
return method.invoke (anInstance);
}
public Object getInstance (String className) throws
ClassNotFoundException,
InstantiationException,
IllegalAccessException {
Class c = Class.forName (className);
return c.newInstance();
}
}
That's it. I would also like not to mess with the ClassLoader unless absolutely necessary. Once I get a handle on how to do this with annotations, I can look into passing arguments and go forward. Thank you for your help.
I wouldn't use Annotations for this - it's probably worth covering what Annotations are and where they are best used.
Instead I would do something like:
Place different files in each jar
A main class to each jar file, which does the same thing: list the files on the classpath
I think that would meet the project requirements.
To list the files on the classpath you could so something like:
public class Main {
public static void main(final String[] args) throws java.lang.Throwable {
final String list = System.getProperty( "java.class.path" );
for (String path : list.split( ";" )) {
java.io.File object = new java.io.File( path );
if ( object.isDirectory() )
for ( String entry : object.list() ) {
java.io.File thing = new java.io.File( entry );
if ( thing.isFile() )
System.out.println( thing );
else if( object.isFile() )
System.out.println( object );
}
}
}
}

Categories