There are different ways to create strings in Groovy - Using single quotes, double quotes, three single quotes, three double quotes and forward slashes. All of them have a purpose. I'll explain the different flavors and when you need to use which one.
1) Single quotes:
Simple:
lst = 'This is my blog post'
println lst
println lst.getClass().name
This is the simplest way to create a string. Here classname returns java.lang.String. The output of this is:
This is my blog post
java.lang.String
Variable Interpolation:
Variable interpolation does not work with single quotes.
postName = "Groovy Strings"
lst = 'This is my blog post $postName'
println lst
println lst.getClass().name
The output of above is:
This is my blog post $postName
java.lang.String
2) Double quotes
Simple:
lst = "This is my blog post"
println lst
println lst.getClass().name
You can also create string using double quotes. Here also class name is java.lang.String. And this behaves same like single quotes. Output is:
This is my blog post
java.lang.String
Variable Interpolation:
But, double quotes also gives us variable interpolation, when needed.
postName = "Groovy Strings"
lst = "This is my blog post $postName"
println lst
println lst.getClass().name
The output of above is shown below. You can see that variable interpolation worked. Also, in this case, since variable interpolation is used, groovy is smart enough to create GStringImpl instead of regular String.
This is my blog post Groovy Strings
org.codehaus.groovy.runtime.GStringImpl
3) Three Single Quotes
Simple:
lst = '''This is my blog post.
I love it.'''
println lst
println lst.getClass().name
This is mainly used to support multi-line Strings. We don't have to use the + operator or the append() method from StringBuffer or StringBuilder anymore. You can see that Strings created using three single quotes is also regular java.lang.String like the one created with single quotes. Output is shown below:
This is my blog post.
I love it.
java.lang.String
Variable interpolation:
Just like single quotes, three single quotes does not support variable interpolation.
postName = "Groovy Strings"
lst = '''This is my blog post.
$postName'''
println lst
println lst.getClass().name
The output of above is:
This is my blog post.
$postName
java.lang.String
4) Three double quotes:
This is similar to three single quotes and is used for supporting multi-line strings. The only difference is, this supports variable interpolation just like single double quotes does.
Simple:
lst = """This is my blog post.
I love it."""
println lst
println lst.getClass().name
Output is shown below. You can see that the class name is regular String.
This is my blog post.
I love it.
java.lang.String
Variable interpolation:
postName = "Groovy Strings"
lst = """This is my blog post.
$postName"""
println lst
println lst.getClass().name
Output is shown below. You can see that class name is GStringImpl.
This is my blog post.
Groovy Strings
org.codehaus.groovy.runtime.GStringImpl
Both three single quotes and three double quotes behave similar to their single quote counterparts except that they support multi-line strings.
Forward Slashes:
Forward slash behaves very similar to single double quotes. It supports variable interpolation. It does not support multi-line strings. One good thing with forward slash string is it does not need any escape characters. It is used to create regular expressions also.
Simple:
lst = /This is my blog post./
println lst
println lst.getClass().name
Output is:
This is my blog post.
java.lang.String
Variable interpolation:
postName = "Groovy Strings"
lst = /This is my blog post - $postName/
println lst
println lst.getClass().name
Output is:
This is my blog post - Groovy Strings
org.codehaus.groovy.runtime.GStringImpl
Escape characters:
pathName = /c:\develop\grooy\myprojectdir/
println pathName
If you use double quotes or single quotes, then you need to escape the slashes i.e pathName = "C:\Develop" will give compile error. It needs to be defined as pathName = "C:\\Develop". Forward slash strings prevent the hassle of providing escape characters.
The output of above is:
c:\develop\grooy\myprojectdir
Regular Expression:
Using forward slashes, also creates a RegEx. For example, /(B/b)log/ creates a RegEx.
lst = "This is my blog post."
pattern = /(B|b)log/
if (lst =~ pattern) {
println 'matching'
} else {
println 'not matching'
}
The output of above is:
matching
=~ returns a matcher and groovy evaluates that to true if matcher has at least one match. Since the matcher had a match in 'blog', the if statement evaluated to true and printed 'matching'.
Conclusion:
We saw several different ways to create Strings in Groovy. This might be confusing for beginners. In the beginning, you might be better off using just double quotes and three double quotes since it supports what single quotes can support and also does variable interpolation. Also, Groovy is smart enough to create the regular java.lang.String if double quotes string does not use variable interpolation.
Saturday, December 26, 2009
Friday, December 25, 2009
Groovy - Different ways to invoke closure or method with closure as parameter
Closure is one of the most powerful features of Groovy. In Groovy, if a method takes closure as a parameter, there are several different ways to invoke the method. Also, there are different ways to invoke a closure. I'll also show some cases which you might expect to work, but don't. These can sometimes be confusing for a beginner. I'll show them with examples here.
Invoking method with closure as parameter:
The below method takes closure as it's second parameter.
def getOddNumbers(n, closure) {
for (int i=1; i<=n; i+=2) {
println "in method ${closure(i)}"
}
}
There are many ways (i.e syntaxes) to invoke this method:
1) getOddNumbers(10, {
println it
it
})
The closure is the second parameter passed to method. All it does is print the number and return that number. Now, we'll see the other ways.
2) getOddNumbers 10, {
println it
it
}
This syntax is same as 1, except that the open and close braces are optional. This really has nothing to do with closure, but I wanted to show this other syntax also.
3) getOddNumbers(10) {
println it
it
}
If closure is the last parameter to the method, then it can be passed as a separate code block like above. This is very elegant and widely used way to pass closure to a method.
4) printingClosure = {
println it
it
}
getOddNumbers(10, printingClosure)
We can assign closure to variable and use that in our method call later. Also,
getOddNumbers 10, printingClosure will work too i.e we can omit braces here. But getOddNumbers(10) printingClosure does not work though.
Invoking Closure:
Also, there are different ways to invoke the closure. In our above method, we used closure(i) to invoke the closure. We could also do closure i to invoke the method. This works as long as we don't use the return value. But, if we do
x = closure i or println closure i, this fails. closure(i) works fine in these cases and is the common way to invoke closure.
Invoking method with closure as parameter:
The below method takes closure as it's second parameter.
def getOddNumbers(n, closure) {
for (int i=1; i<=n; i+=2) {
println "in method ${closure(i)}"
}
}
There are many ways (i.e syntaxes) to invoke this method:
1) getOddNumbers(10, {
println it
it
})
The closure is the second parameter passed to method. All it does is print the number and return that number. Now, we'll see the other ways.
2) getOddNumbers 10, {
println it
it
}
This syntax is same as 1, except that the open and close braces are optional. This really has nothing to do with closure, but I wanted to show this other syntax also.
3) getOddNumbers(10) {
println it
it
}
If closure is the last parameter to the method, then it can be passed as a separate code block like above. This is very elegant and widely used way to pass closure to a method.
4) printingClosure = {
println it
it
}
getOddNumbers(10, printingClosure)
We can assign closure to variable and use that in our method call later. Also,
getOddNumbers 10, printingClosure will work too i.e we can omit braces here. But getOddNumbers(10) printingClosure does not work though.
Invoking Closure:
Also, there are different ways to invoke the closure. In our above method, we used closure(i) to invoke the closure. We could also do closure i to invoke the method. This works as long as we don't use the return value. But, if we do
x = closure i or println closure i, this fails. closure(i) works fine in these cases and is the common way to invoke closure.
Java to Groovy Incompatibilities
I'm fairly new to Groovy. One of the things which I've heard many times (from various sources) is that I can take a Java file, rename it as .groovy and can run it. While this is true for the most part, there are some incompatibilities, which caught me by surprise. The following list may not be exhaustive, but I'm listing the incompatibilities which I've read from books/online sources or came across while coding:
- If your Java code uses == (reference equal), then groovy does not treat it the same. In groovy == is (almost) equivalent to using .equals() method. Even this also first uses the compare() method (if present) and if that is not present, then it uses equals() method in the class to find out if the two objects are equal. If you want to compare the references of two objects, then you need to use is() in Groovy. For example, obj1.is(obj2) will return true if the obj1 and obj2 point to the same reference.
- def and in are keywords in Groovy. So, if your Java code has these variable names, then you may encounter issues. Also, it has a special meaning (closure parameter) in Groovy. If you have a variable named it, you might have issues.
- Code blocks like this are not supported by Groovy. It gets confused since it looks like a closure. { System.out.println("Code Block") }
- Declaring an array like int[] arr = new int[] { 10, 20, 30 } does not work in Groovy. In Groovy, it needs to be declared like int[] arr = [10, 20, 30]
- In Java, both public static void main(String args[]) and public static void main(String[] args) will work i.e the placement of [] does not matter. However, in Groovy, the former one does not work. Only String[] args will work in Groovy.
- As of Groovy 1.6, inner classes are not supported. They are now supported in 1.7. See this in Groovy 1.7 release notes.
- Even in 1.7, the behavior of inner class is not exactly the same. For example, if the nested class (B) need to refer an instance variable (say counter_) from outer class (A), in Java you can do this using A.this.counter_. This syntax does not work in Groovy.
Thursday, December 3, 2009
What did I learn today - Adding new style tabs in Eclipse RCP application
If you are building an Eclipse RCP application, by default all of the views and editors would have the traditional old style rectangular tabs. If you want curvy ended tabs, like the one in Eclipse IDE, you can implement that by setting this preference value in PlatformUI preference store. Just add the following line to preWindowOpen() method in your ApplicationWorkbenchWindowAdvisor.
PlatformUI.getPreferenceStore().setValue(
IWorkbenchPreferenceConstants.SHOW_TRADITIONAL_STYLE_TABS, false);
By default, traditional style tabs are set to true. You need to turn it OFF to get new style tabs.
PlatformUI.getPreferenceStore().setValue(
IWorkbenchPreferenceConstants.SHOW_TRADITIONAL_STYLE_TABS, false);
By default, traditional style tabs are set to true. You need to turn it OFF to get new style tabs.
Wednesday, November 25, 2009
Eclipse BIRT plugin - Chart scaling issue
Background:
We are usingEclipse BIRT plugin 2.5.0 in our application. We encountered a strange issue with BIRT. As you would normally do, we set the scaling in our BIRT chart (in report design) to AUTO, so that BIRT can dynamically figure out the min/max and step size for the axis. It worked fine until we hit a corner case. I wanted to share my problem and the workaround I used to solve this problem so that it helps anyone having the same issue.
Problematic case:
In one of our cases, all the values for the axis were either 0 or less than 0. In this case, it was between 0 and -.02 and so when displayed in percent it was from 0 to -2%. In this case, BIRT chose the axis scale to be from 0 to -100% and so the chart got squeezed (since the entire range is only between 0 and -2%), making the chart unusable. This seems like a bug in BIRT. I've just filed a bug for this: https://bugs.eclipse.org/bugs/show_bug.cgi?id=296207
Workaround:
Aftering unsuccessfully googling for some time and searching for other alternatives, I tried setting the step number and that solved the problem. This made BIRT choose the proper min/max and enlarged my chart.
If you are in report designer, edit chart and go to Format chart tab (or right click on chart and select Format chart). Under Chart Area->Axis, select the axis and hit scale button. This will open "Axis Scale" window. Here instead of "Auto" select the "Step Number" radio button. Specify a number here (10, 15 etc.,) to indicate the number of steps in the axis. This should add the following to the xml source of chart in rptdesign.
<Scale>
<StepNumber>10</StepNumber>
</Scale>
This solved my problem. Note that this workaround maintains the dynamic nature and does not hard code the chart values. The min/max (and so the step size) values in the chart are still dynamic based on the values in the chart.
We are using
Problematic case:
In one of our cases, all the values for the axis were either 0 or less than 0. In this case, it was between 0 and -.02 and so when displayed in percent it was from 0 to -2%. In this case, BIRT chose the axis scale to be from 0 to -100% and so the chart got squeezed (since the entire range is only between 0 and -2%), making the chart unusable. This seems like a bug in BIRT. I've just filed a bug for this: https://bugs.eclipse.org/bugs/show_bug.cgi?id=296207
Workaround:
Aftering unsuccessfully googling for some time and searching for other alternatives, I tried setting the step number and that solved the problem. This made BIRT choose the proper min/max and enlarged my chart.
If you are in report designer, edit chart and go to Format chart tab (or right click on chart and select Format chart). Under Chart Area->Axis, select the axis and hit scale button. This will open "Axis Scale" window. Here instead of "Auto" select the "Step Number" radio button. Specify a number here (10, 15 etc.,) to indicate the number of steps in the axis. This should add the following to the xml source of chart in rptdesign.
<Scale>
<StepNumber>10</StepNumber>
</Scale>
This solved my problem. Note that this workaround maintains the dynamic nature and does not hard code the chart values. The min/max (and so the step size) values in the chart are still dynamic based on the values in the chart.
Sunday, November 22, 2009
What did I learn today - Using ArrayContentProvider (Eclipse RCP - JFace)
If your table view displays a collection of objects for example, collection of Person objects, Collection of Employee info etc., instead of writing your own content provider, consider using ArrayContentProvider. This takes the viewer input (collection) and converts it to an Array.
For example, say your code does something like this:
this.viewer.setContentProvider(new EmployeeInfoContentProvider());
this.viewer.setLabelProvider(new EmployeeInfoLabelProvider());
// Assume employeeInfoSet is a Set of EmployeeInfo objects
this.viewer.setInput(this.employeeInfoSet);
EmployeeInfoContentProvider code:
public class EmployeeInfoContentProvider implements IStructuredContentProvider {
@Override
public Object[] getElements(final Object inputElement) {
if (inputElement instanceof Set) {
return ((Set) inputElement).toArray();
}
return new Object[0];
}
@Override
public void dispose() {
//Nothing to do
}
@Override
public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) {
//Nothing to do
}
}
In the above code, EmployeeContentProvider does nothing but translating the collection to array. In this case, you can remove EmployeeContentProvider and use ArrayContentProvider, which does the conversion for you. So, your view code would look like this:
// Replacing EmployeeInfoContentProvider with ArrayContentProvider
this.viewer.setContentProvider(new ArrayContentProvider());
this.viewer.setLabelProvider(new EmployeeInfoLabelProvider());
// Assume employeeInfoSet is a Set of EmployeeInfo objects
this.viewer.setInput(this.employeeInfoSet);
Less code is better right!
For example, say your code does something like this:
this.viewer.setContentProvider(new EmployeeInfoContentProvider());
this.viewer.setLabelProvider(new EmployeeInfoLabelProvider());
// Assume employeeInfoSet is a Set of EmployeeInfo objects
this.viewer.setInput(this.employeeInfoSet);
EmployeeInfoContentProvider code:
public class EmployeeInfoContentProvider implements IStructuredContentProvider {
@Override
public Object[] getElements(final Object inputElement) {
if (inputElement instanceof Set
return ((Set
}
return new Object[0];
}
@Override
public void dispose() {
//Nothing to do
}
@Override
public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) {
//Nothing to do
}
}
In the above code, EmployeeContentProvider does nothing but translating the collection to array. In this case, you can remove EmployeeContentProvider and use ArrayContentProvider, which does the conversion for you. So, your view code would look like this:
// Replacing EmployeeInfoContentProvider with ArrayContentProvider
this.viewer.setContentProvider(new ArrayContentProvider());
this.viewer.setLabelProvider(new EmployeeInfoLabelProvider());
// Assume employeeInfoSet is a Set of EmployeeInfo objects
this.viewer.setInput(this.employeeInfoSet);
Less code is better right!
Sunday, November 8, 2009
What did I learn today - Adding key listeners to trees and tables (SWT/JFace)
I wanted my trees and tables to listen to DEL key so that when user presses it, the selected nodes in the tree or the selected cells in the table would get deleted. I was looking for the method to add key listener to the table/tree viewer, but found that I need to add it to the viewer control.
Below is the code to add listener to the delete key on your tree viewer.
this.treeViewer.getControl().addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(final KeyEvent e) {
if (e.keyCode == SWT.DEL) {
IStructuredSelection selection = (IStructuredSelection) treeViewer.getSelection();
if (selection.isEmpty()) {
return;
}
// Business logic to handle deletion - remove from model, view etc.,
handleDelete(selections);
}
}
});
You can do it similarly for other viewers (table viewer etc.,) or to listen to other keys also.
If you are working directly with SWT, you will add listener directly to the tree/table (i.e the control itself) like tree.addKeyListener(), table.addKeyListener() etc., You can add this to any control in SWT.
Below is the code to add listener to the delete key on your tree viewer.
this.treeViewer.getControl().addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(final KeyEvent e) {
if (e.keyCode == SWT.DEL) {
IStructuredSelection selection = (IStructuredSelection) treeViewer.getSelection();
if (selection.isEmpty()) {
return;
}
// Business logic to handle deletion - remove from model, view etc.,
handleDelete(selections);
}
}
});
You can do it similarly for other viewers (table viewer etc.,) or to listen to other keys also.
If you are working directly with SWT, you will add listener directly to the tree/table (i.e the control itself) like tree.addKeyListener(), table.addKeyListener() etc., You can add this to any control in SWT.
Subscribe to:
Posts (Atom)