donderdag 17 december 2009

Proper handling of linked resources

In the Eclipse workbench you can import resources, meaning copying them into the workspace, or you can set up a folder to link to them, and they files stay where they were. For large datasets, linking has obvious advantages. There is even a Decorator to show linked resources.

When programming in Eclipse you sometimes must use plain Java based libraries. These libraries handle files using java.io.File. When you work in Eclipse you reference files in the workspace using org.eclipse.core.resources.IFile. So in this case I needed to convert between the two formats.

First attempt, WRONG!

My first attempt was to use f.getFullPath().toFile() and that worked! Because I tested all my code with small data sets in the workspace, it wasn't until later that I found out, this does not work with linked resources:
java.io.FileNotFoundException: /Remote-Data/eulumdat/data.file (No such file or directory)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.(FileInputStream.java:106)
at java.io.FileReader.(FileReader.java:55)
at xxxx

Second attempt: the proper way

My went back to the offending code and used CTRL+SPACE again. Now I selected f.getLocation().toFile() and that did it!

Conclusion

In order to get your code working in all circumstances, you need to test with both resources in the workspace and outside.
I'm i good company though, as there are 64 open bugs in the Eclipse bugzilla about linked resources.
Also I suspect code that doesn't work with linked resources, will not work with the remote system explorer, like this one.

donderdag 10 december 2009

Dutch Eclipse Democamp 2009 at TSI International Nieuwegein

Yesterday TSI International hosted the Eclipse Democamp for about 40 people in Nieuwegein.

Photographic impression


Wim Jongman preparing for the kick off on Eclipse 4.0
Advanced networking facilities
All the chairs in the building set up
Making clear who paid for food and drink
and quite crowd came to listen and watch
Jeroen van Grondelle and Marcel Offermans about OSGI service patterns. (Only implemented in full in Apache Felix), Jeroen stressed that we have to rethink our application for OSGi to fully take advantage of its facilities and not continue in our old Eclipse habits. Think service, not listener.




Jim van Dijk on Presentation Modelling Framework. We have to learn to think about UI in a whole different way again, not in terms of implementation (widgets, controls, HTML, ...) but in their abstractions: conversations, dialogs, compound dialogs, etc.

Break for food and drink, THANK YOU Wim Jongman!
That went down just as well as the presentations
Roel Spilker and Reinier Zwitserloot talked about Project Lombok and showed the Lombox Eclipse plugin to the world for the first time!
Wim Jongman and Marcel Offermans talked about and demonstrated OSGi in the cloud using Eclipse and Apache ACE.
Finally we heard and saw Jelle Herold about Verostko graphics toolkit & Statebox process engine.

Conclusion

A well hosted gathering with excellent presentations for a growing number of Eclipse enthusiasts.

Decorating your Jobs

Reinforcing your brand with every UI contact of your plugin is important in this competitive world. Many plugins launch Jobs when when Eclipse is starting up, to refresh their data, check the license, or whatever. Some Jobs display an icon to reveal the identity and reinforce the brand. Below is an example, showing two jobs that use text only and one with an icon.So how is this achieved?

step 1: register with Workbench ProgressService

An Eclipse Job can belong to a family, where a family can be any java Object. The ProgressService maintains a table of icons associated with each family. So step one is to register your icon and family in your Activator's start() method.
@Override
public void start(final BundleContext context) throws Exception {
super.start(context);
getWorkbench().getProgressService().
registerIconForFamily(
getImageDescriptor(ICONPATH),
MyTools.PLUGIN_ID);
[...]
Rebuilder rebuilder = new Rebuilder("Initializing My Tools");
rebuilder.schedule(20L);
}

step 2: override belongsTo() in your Job subclass

Next you override the method belongsTo() in your subclass with some simpel logic, calling super.belongsTo()when not equal top allow for Job class hierarchies.
public class Rebuilder extends Job {
[...]
@Override
public boolean belongsTo(final Object family) {
if (family.equals(MyTools.PLUGIN_ID)) {
return true;
}
return super.belongsTo(family);
}
[...]
}

Conclusion

As always in Eclipse programming this solution took a long time to find, but can be implemented in a few lines of code once you know how.

Caveat

What remains is that you can only do this for Jobs that you launch yourself, and not for other background tasks like the Auto Build jobs that call your Builders.
They all share the same icon :-(
I have created a bug for this, please support and vote here.

vrijdag 4 december 2009

WorkbenchMarkerResolution is fantastic!

One of my ongoing projects that is in beta now for a long time is EulumdatTools, a special purpose editor and workbench for managing, verifying and editing EULUMDAT files. EULUMDAT is a European de facto standard for photometric data files. It describes stuff like manufacturer, product name, lamp type, power consumption and luminous flux distribution for lighting products.

Builder, IMarker and IMarkerResolution2


I used Eclipse to create a Builder, IMarkers and IMarkerResolution2 to create a Validator, Problems view entries and Quick Fix solutions under CTRL/CMD+1 to resolve issues where file were not conforming to standard or incomplete (for the curious: some manufacturers do not provide the Direct Flux Factors).
There are 10 of these Direct Flux Factors in every file, so it would be nice to be able to fix these all in one action.

WorkbenchMarkerResolution to the rescue!

After some research I found that that is easier to implement than I first thought! What you must do is make your Quick Fix code extend WorkbenchMarkerReslution instead of implement IMarkerResolution2.
This means you must implement one extra method: IMarker[] findOtherMarkers(IMarker[] markers). You receive ALL of the markers in the problems view and must return an array of those you can handle in this Quick Fix, this is easy to implement with a loop and a isValidOther() method.
public class ReplaceDFF extends WorkbenchMarkerResolution {

private final IMarker originalMarker;

// use constructor to remember original marker
public ReplaceDFF(final IMarker marker) {
super();
originalMarker = marker;
}

@Override
public IMarker[] findOtherMarkers(IMarker[] markers) {
List<IMarker> others = new ArrayList<IMarker>();
for (IMarker marker : markers) {
if (isValidOther(marker)) {
others.add(marker);
}
}
return others.toArray(new IMarker[0]);
}

@Override
public boolean isValidOther(final IMarker marker) {
// is it the originalMarker, we don't want duplicates!
if(markerToCheck.equals(originalMarker)) {
return false;
}
// is it in the same file as original marker?
if(!marker.getResource().equals(originalMarker.getResource())) {
return false;
}
// is it the same validator?
String checkerName = LightOutputRatioChecker.class.getName();
if(!checkerName.equals(getCheckerName(marker))) {
return false;
}
// is it the same error found?
String checkerMessage = getCheckerMessage(marker);
if(!checkerMessage.startsWith(
LightOutputRatioChecker.DIRECT_RATIO_1))
{
return false;
}
return true;
}
[...]
}

The result is improved usability

Running with this small modification gives us this Quick Fix Dialog:
In practice the values are all correct or more than one is wrong, so handling this in one Quick fix will improve the users productivity in a big way. Click once, save the file and all warnings are gone...

Final notes...

I experimented with various filtering strategies before settling on fixing all similar errors in one file. Using the filters you can also opt to:
  • Fix different errors all in the same file, when your run(IMarker) method can handle different errors.
  • Find all errors in the workspace, letting through all markers with this fix. But then each fix required the file to be opened, so that takes long time and this cannot be cancelled.
  • I may still try to check whether the file is already open and only suggest to apply the fix in these.