Programming for gvSIG CE

From gvSIG CE Wiki

Jump to: navigation, search

This page is work in progress.

Follow this link to get to the continuously updated API documentation pages (Javadoc).

Meanwhile, here are some pointers to external resources:

Contents

Good practice

The gvSIG CE code base is the result of many years of work, done by many programmers of varying skill levels and backgrounds. As a result, there is a lot inhomogenity that should be avoided in the future. Please read the following carefully. It will help you produce good and fast code (thanks to L. Bourg├Ęs for providing much of the insight on this page).

Code formatting and Character encoding

Use a good code formatter to automatically reformat all the code to help readability (e.g. Eclipse produces nice results). Consider using longer lines (120 chars) to keep automatically wrapped source code nice and readable.

All source code should be encoded as UTF-8. We currently have a lot of invalidly encoded text using non-UTF-8 Spanish accent characters. If you spot them, please correct them manually or automatically using a tool such as awk. To avoid future problems, use only ASCII characters (best) or (if you really have to) use Unicode notation, such as "\u2202"!

Exception handling and debug output

Exceptions are costly (they need to capture a complete stack trace) and should only be used for catching exceptional (hence the name!) behaviour, not for providing regular return values!

Always log all exceptions (never just ignore them or add a comment) use log4j or slf4j; maybe at debug level to avoid loosing exceptions.

Avoid Throwable.printStackTrace() because it is very slow and exceptions are lost (i.e. they won't be present in log files): use log4j or slf4j instead.

Resource handling

Always release resources (files, datasources, streams ...) using the try...finally pattern or (Java 7 and up) try-with-resources! There are already many cases in our codebase where resources are potentially leaking in case of exceptions:

  • Graphics: always call g.dispose() for every img.createGraphics(): ~ 70 occurences in total
  • Recordset: always call stop() for every start() in finally block.
  • Streams: always close() them in a finally block! Idem for NIO channels.

... please do not add any more of these potential leaks, as they can result in seemingly random bugs when exceptions occur!

Logs vs System.out

System.out calls are buffered and synchronized ("flushed") this may slow down concurrent threads. Moreover, messages to System.out are not preserved in log files: use log4j or slf4j instead.

Loops and initializations

Avoid calling invariant methods in loop conditions, such as collection.size() or "getter" methods:

 for (int i = 0; i < list.size (); i++)

... should better be:

 for (int i = 0, size=list.size (); i < size; i++)

Initialize collections and buffers with sufficient initial capacity to avoid many array growing operations later (growing an array requires a copy plus a garbage collection operation!):

 new ArrayList (5000) // 10 by default
 new StringBuilder (2048) // 32 by default

Programming hints

This is a loose collection of hints that cover common programming tasks in gvSIG CE. They have been collected from various sources.

Checking for gvSIG CE

Since gvSIG CE is a fork of the original gvSIG codebase, it is bound to become incompatible with the former. To check whether your code is running under CE, the following version checker can be used:

 import java.lang.reflect.Method;
 import com.iver.cit.gvsig.Version;
 
 public class VersionChecker {
 
   public static boolean isGvSIGCE() {
     try {
       final Method method = Version.class.getMethod("isGvSIGCE");
       return true;
      }
      catch (final Exception e) {
        return false;
     }
   }
 }

Working with layers

Retrieving file name and path information from an FLyr object:

 RasterAdapter adap = ((FLyrRaster) layer).getSource();
 File file = ((RasterFileAdapter) adap).getFile();
 file.getPath()

If the layer needs to dispose resources when done (e.g. close filehandles, delete temporary data, etc.), then it should implement the interface com.iver.cit.gvsig.fmap.Disposable and register itself with the generic resource cleaner class com.iver.cit.gvsig.fmap.layers.ResourceCleaner. E.g. this is what the constructor for com.iver.cit.gvsig.fmap.layers.VectorialFileAdapter does:

 public VectorialFileAdapter(File file) {
   super();
   this.file = file;
   // register this new instance:
   this.vfdCleanRef = new VectorialFileDriverCleaneableReference(this);
   ResourceCleaner.getInstance().register(this.vfdCleanRef);
 }

For listening to events in a layer collection (layer/s added, removed, hidden, etc.), implement com.iver.cit.gvsig.fmap.layers.LayerCollectionListener. E.g. this is done by com.iver.cit.gvsig.project.documents.view.toc.gui.TOC to refresh the TOC when a layer is added ...

 @Override
 public void layerAdded(LayerCollectionEvent e) {
   refresh();
 }

... and the SEXTANTE Toolbox (es.unex.sextante.gvsig.gui.ToolboxDialog) uses this to get all View windows in the current project and attach listeners to their layer collections, so that the list of available tools can be updated depending on the data present in the project:

 private void addListeners() {
     //Register a propertyChangeListener to capture events when a new document is added in gvSIG
     final Project p = ((ProjectExtension) PluginServices.getExtension(ProjectExtension.class)).getProject();
     p.addPropertyChangeListener(this);
     //Register addLayerCollectionListener in existing views
     final IWindow[] window = PluginServices.getMDIManager().getAllWindows();
     for (int i = 0; i < window.length; i++) {
        if (window[i] instanceof BaseView) {
           final FLayers layers = ((BaseView) window[i]).getMapControl().getMapContext().getLayers();
           if (listLayers.indexOf(layers) == -1) {
              layers.addLayerCollectionListener(this);
              listLayers.add(layers);
           }
        }
     }
  }

MDI window management

Listening to changes in the project

Implement com.iver.cit.gvsig.project.documents.ProjectDocumentListener to react to new windows being created in the MDI. E.g. the SEXTANTE Toolbox (es.unex.sextante.gvsig.gui.ToolboxDialog) uses this to attach new layer listeners every time a new View is added to a project:

 public void windowCreated(final IWindow window) {
   if (window instanceof BaseView) {
     final FLayers layers = ((BaseView) window).getMapControl().getMapContext().getLayers();
     if (listLayers.indexOf(layers) == -1) {
       layers.addLayerCollectionListener(this);
       listLayers.add(layers);
     }
   }
 }