Use HashSet with a user defined class in an Xtext validator - dsl

I'm writing a small DSL that stores a list of coordinates:
Model:
locations+=Coordinates*
;
Coordinates:
'(' x=INT ',' y=INT ')'
;
I'm working now on the validator, and one of the things I want to make sure is that the list does not contain the same location (set of coordinates) twice. For this, I've written the following validator check:
#Check
def checkLocation(Model model) {
val locationSet = newHashSet
for (location : model.locations) {
if (!locationSet.add(location)) {
error('Locations must be unique.', location, LOCATION)
}
}
}
So that the locations get added to a HashSet and an error occurs if the same locations gets added twice. However, this doesn't work (the error never gets triggered). I'm guessing because the class Coordinates does not define the hashCode and equals methods that are required for the HashSet to work.
Any ideas on how could I make the HashSet work? Is it possible to define the implementation of the hashCode and equals methods of the generated class?

Related

Unable to display the age of an object that was passed to a kotlin function

I'm just starting to learn kotlin and ran into a problem:
I have a Person class that has two fields
-age (Int data type)
-name (data type String)
there is also a oldUp function where I pass a Person object and increment the object's age field by 10.
Before the end of the program ** I want to display the age of the object that was passed to oldUp **
However, age is not shown.
my code:
class Person(var name: String, var age: Int){
}
fun growOld(human: Person){
human.age+=10
}
fun main(args: Array<String>) {
var human = Person("Kitty",6)
growOld(human)
println(human)
}
If you want to print the age, you can just write: println(human.age).
In your example it might be cleaner to add the growOld method to your class so you can call it on the object. For example:
class Person(var name: String, var age: Int){
fun growOld() {
this.age += 10
}
}
fun main() {
var human = Person("Kitty", 6)
println(human.age) // prints 6
human.growOld()
println(human.age) // prints 16
println(human.name) // prints Kitty
}
The problem is you're trying to print the human object itself. Under the hood, this calls its toString() method - every class has one of these, because it's defined on the type all classes derive from. If you don't override it and provide a nice way to "pretty print" your object, it'll use the default implementation, which is basically a reference to the object in memory.
A lot of classes you use have a nice toString() implementation, e.g. if you print a List you get ["something", "that", "looks", "like", "this"]. But that behaviour needed to be coded in - and you need to do that for your Person class too!
So you can override the default implementation like this:
override fun toString(): String {
// return a String here
}
override means you're taking an existing function and writing your own version of it to use instead - if this doesn't match an existing function you can override, you'll get an error. You'll also get an error if you don't use the override keyword for a function that looks exactly like an existing one in a supertype - it's just to make sure you don't accidentally do the wrong thing. In IntelliJ you can do Ctrl+O to override existing functions if you like.
So you could do something like this:
// inside your Person class
override fun toString(): String {
return "Name: $name, age: $age"
}
and then when you use it in a print statement, or in a string (like "Details: $person" or val details = "Details: " + person) it will call that toString() method and get the string you produced.
Another way to approach this is to use a data class:
data class Person(var name: String, var age: Int)
A data class is a special kind of class where all your "data" goes in the constructor (as properties, either val or var), and then you get some boilerplate stuff for free which uses those properties (and only those properties). Things like an equals() and hashCode() implementation that uses that data - and the relevant thing here, it gives you a toString() implementation that pretty prints name and age. Try it out!
Data classes can be really handy for simple data objects like you have here - but in normal classes, overriding toString() yourself is the general way of doing things. And you can still override a data class's toString if you want - sometimes you might want a more complex representation, or nice formatting, or you might want to only include some properties and ignore others. You're in control of how it prints itself!
And if you just want to print the age property, or print anything at all using the data in your object, then you just need to do what Robin's answer says. You don't need a toString() implementation at all for that (and since this is how you usually use objects, often you won't need to write a toString for your own classes at all)

Is it possible to get the name of variable in Groovy?

I would like to know if it is possible to retrieve the name of a variable.
For example if I have a method:
def printSomething(def something){
//instead of having the literal String something, I want to be able to use the name of the variable that was passed
println('something is: ' + something)
}
If I call this method as follows:
def ordinary = 58
printSomething(ordinary)
I want to get:
ordinary is 58
On the other hand if I call this method like this:
def extraOrdinary = 67
printSomething(extraOrdinary)
I want to get:
extraOrdinary is 67
Edit
I need the variable name because I have this snippet of code which runs before each TestSuite in Katalon Studio, basically it gives you the flexibility of passing GlobalVariables using a katalon.features file. The idea is from: kazurayam/KatalonPropertiesDemo
#BeforeTestSuite
def sampleBeforeTestSuite(TestSuiteContext testSuiteContext) {
KatalonProperties props = new KatalonProperties()
// get appropriate value for GlobalVariable.hostname loaded from katalon.properties files
WebUI.comment(">>> GlobalVariable.G_Url default value: \'${GlobalVariable.G_Url}\'");
//gets the internal value of GlobalVariable.G_Url, if it's empty then use the one from katalon.features file
String preferedHostname = props.getProperty('GlobalVariable.G_Url')
if (preferedHostname != null) {
GlobalVariable.G_Url = preferedHostname;
WebUI.comment(">>> GlobalVariable.G_Url new value: \'${preferedHostname}\'");
} else {
WebUI.comment(">>> GlobalVariable.G_Url stays unchanged");
}
//doing the same for other variables is a lot of duplicate code
}
Now this only handles 1 variable value, if I do this for say 20 variables, that is a lot of duplicate code, so I wanted to create a helper function:
def setProperty(KatalonProperties props, GlobalVariable var){
WebUI.comment(">>> " + var.getName()" + default value: \'${var}\'");
//gets the internal value of var, if it's null then use the one from katalon.features file
GlobalVariable preferedVar = props.getProperty(var.getName())
if (preferedVar != null) {
var = preferedVar;
WebUI.comment(">>> " + var.getName() + " new value: \'${preferedVar}\'");
} else {
WebUI.comment(">>> " + var.getName() + " stays unchanged");
}
}
Here I just put var.getName() to explain what I am looking for, that is just a method I assume.
Yes, this is possible with ASTTransformations or with Macros (Groovy 2.5+).
I currently don't have a proper dev environment, but here are some pointers:
Not that both options are not trivial, are not what I would recommend a Groovy novice and you'll have to do some research. If I remember correctly either option requires a separate build/project from your calling code to work reliable. Also either of them might give you obscure and hard to debug compile time errors, for example when your code expects a variable as parameter but a literal or a method call is passed. So: there be dragons. That being said: I have worked a lot with these things and they can be really fun ;)
Groovy Documentation for Macros
If you are on Groovy 2.5+ you can use Macros. For your use-case take a look at the #Macro methods section. Your Method will have two parameters: MacroContext macroContext, MethodCallExpression callExpression the latter being the interesting one. The MethodCallExpression has the getArguments()-Methods, which allows you to access the Abstract Syntax Tree Nodes that where passed to the method as parameter. In your case that should be a VariableExpression which has the getName() method to give you the name that you're looking for.
Developing AST transformations
This is the more complicated version. You'll still get to the same VariableExpression as with the Macro-Method, but it'll be tedious to get there as you'll have to identify the correct MethodCallExpression yourself. You start from a ClassNode and work your way to the VariableExpression yourself. I would recommend to use a local transformation and create an Annotation. But identifying the correct MethodCallExpression is not trivial.
no. it's not possible.
however think about using map as a parameter and passing name and value of the property:
def printSomething(Map m){
println m
}
printSomething(ordinary:58)
printSomething(extraOrdinary:67)
printSomething(ordinary:11,extraOrdinary:22)
this will output
[ordinary:58]
[extraOrdinary:67]
[ordinary:11, extraOrdinary:22]

Nomin automap causes infinite loop

I am using Nomin for mapping tasks. As taken from the documentation of Nomin it should be able to map fields with the same name by itself in case automapping has been activated. When activating it, it causes an infinite loop exception.
I have the following:
mappingFor a: CoinsOnMarketPlace, b: Coin
// automap() // when deactivated it works fine, when activated infinite loop
a.coin.name = b.name
a.coin.rank = b.rank
a.priceUSD = b.priceUSD // Could be automapped
a.priceBTC = b.priceBTC // Could be automapped
...
Exception:
org.nomin.core.NominException: ./net/hemisoft/ccm/repository/coinmarketcap2coin.groovy: Recursive mapping rule a = b causes infinite loop!
One thing worth adding regarding your use case - this Recursive mapping rule a = b causes infinite loop! exception is thrown because you use groovy classes in your mapping rule. Nomin uses ReflectionIntrospector and what's important:
It performs getting/setting properties using accessor methods which are called through the Java reflection mechanism. ReflectionIntrospector uses supplied NamingPolicy instance to determine accessor methods. JbNamingPolicy is used by default, this implementation cerresponds the JavaBeans convention. Its InstanceCreator named ReflectionInstanceCreator instantiates objects using Class.newInstance().
Source: http://nomin.sourceforge.net/introspectors.html
A simple Groovy class like:
class Entity {
String name
String somethingElse
}
gets compiled to a Java class that implements GroovyObject providing following methods:
public interface GroovyObject {
Object invokeMethod(String var1, Object var2);
Object getProperty(String var1);
void setProperty(String var1, Object var2);
MetaClass getMetaClass();
void setMetaClass(MetaClass var1);
}
In this case ReflectionInstanceCreator combined with automap() resolves following mappings:
a.property = b.property
and
a = b
where a = b mapping comes from MetaClass getMetaClass() getter method I suppose, because there is no mapping like a.metaClass = b.metaClass resolved. a.property = b.property gets resolved because of Object getProperty(String var1) method.
Solution
This problem can be solved by specifying explicitly ExplodingIntrospector for your mapping script that:
It performs getting/setting properties using a class field immediately through through the Java reflection mechanism and may be useful in case when domain object don't provide accessors for their properties. Supplied instance creator is ReflectionInstanceCreator.
Source: http://nomin.sourceforge.net/introspectors.html
All you have to do is to add
introspector exploding
right below mappingFor a: ..., b: ... header. For example:
import mypackage.Entity
import mypackage.EntityDto
mappingFor a: Entity, b: EntityDto
introspector exploding
automap()
a.test2 = b.test1
Tested with two Groovy classes, worked like a charm. Hope it helps.

Runtime meta programming in ceylon

I get unexpected results from the following code snippet using the eclipse ide:
class Example(String s = "init") {
shared String a() => "Func 1";
shared String b = "Attr 1";
shared String c(Integer i) { return "Func 2"; }
}
shared
void run() {
// 1.
print("getAttributes: " + `Example`.getAttributes().string);
print("getMethods: " + `Example`.getMethods().string);
// prints: []
// i.e. doesnt print any attribute or method
// 2.
value e = Example()
type(e); // error
e.type(); // error, should be replaced by the ide with the above version.
}
ad 1.) I get as a result:
getAttributes: []
getMethods: []
where I expected lists containing attributes or methods.
ad 2.) The doc says:
"The type() function will return the closed type of the given instance, which can only be a ClassModel since only classes can be instantiated.
..."
But I cant find the type() function and others related to meta programming, i.e. I dont get a tooltip, but instead I get a runtime (!) error:
Exception in thread "main" com.redhat.ceylon.compiler.java.language.UnresolvedCompilationError: method or attribute does not exist: 'type' in type 'Example'
So, where is the equivalent to the backticks `Example`.... as a function ?
`Example`.getAttributes() returns an empty list because getAttributes takes three type arguments: Container, Get, Set. When called as getAttributes(), the typechecker tries to infer them, but since there’s no information (no arguments with the appropriate type), the inferred type argument is Nothing. Since Nothing is not a container of any of the class’ members, the resulting list is empty. Use getAttributes<>() instead to use the default type arguments, or specify them explicitly (e. g. getAttributes<Example>()). Same thing with getMethods(). Try online
The type function is in ceylon.language.meta and needs to be imported: import ceylon.language.meta { type }. Once you do that (and remove the e.type() line), the compilation error will go away.
If you directly want a meta object of the function, you can write `Example.a`.

Issues adding source code to a custom DSL file programatically

I currently have a xtext grammar that looks like the following :
Features:
'feature' name = ID
'{'(
('action' '{' action+=Actions (',' action+=Actions)* '}')? &
('dependencies' '{' dependencies = Dependencies '}')? &
('children' '{' children = Children '}')?
)'}'
;
What I want to do with this is add an action to an already existing source file programatically, for that I am using the IUnitOfWork.Void class that I subclass for easier implementation , it currently looks like this (the meaningful part of it) :
final XtextEditor editor = (XtextEditor)sourcepart;
final IXtextDocument document = editor.getDocument();
document.modify(new IUnitOfWork.Void<XtextResource>(){
public void process (XtextResource resource) throws Exception {
IParseResult parseResult = resource.getParseResult();
if(parseResult ==null)
return;
CompositeNode rootNode=(CompositeNode) parseResult.getRootNode();
LeafNode node = (LeafNode)NodeModelUtils.findLeafNodeAtOffset(rootNode, 0);
EObject object =NodeModelUtils.findActualSemanticObjectFor(node);
Through this I traverse the tree of the model and get to my Features object to which I want to add an action to (this is done through a pop up menu in a custom Tree View I'm implementing)
Here's my problem : whenever I want to add an action it screws up the way the tags are placed in the source file , and by that I mean that instead of :
action {
act1.set (foo),
act2.set (bar),
act3.set (baz),
act4.set (booze) //where this is the new action that I add
}
it will add it as
action {
act1.set (foo),
act2.set (bar),
act3.set (baz)
}
action {
act4.set(booze)
}
And this is illegal by the rules of my grammar, and I'm not allowed to change the way it should be written. (I am allowed to make small changes to the way the rules are implemented, but would really want to avoid it as it would mean a whole new amount of work to reimplement other things that depend on them)
I've tried :
adding it directly through Features.getAction().add(*the new action);
copying the items in the list into an array with the toArray() method so as to avoid referencing, adding my action to the array, clearing the list then adding all the elements again one by one
creating an entirely new Features object and setting everything in it to be the same as the currently edited one then replacing the feature with the new one
And I'm out of ideas after that. The frustrating part is that the 3rd method worked for a different kind of object in my grammar and had no errors there.
How could I make this work ?
this is a bug in xtext. (can you please file a ticket?)
as a workaround you may use the following
Features:
'feature' name = ID
'{'(
('action' '{' actionList=ActionList '}')? &
('dependencies' '{' dependencies = Dependencies '}')? &
('children' '{' children = Children '}')?
)'}';
ActionList:
(action+=Action (',' action+=Action)*)
;

Resources