Coding and development rules

Forewords

This document describes a list of coding conventions that are required for code submissions to the project. By default, the coding conventions for most Open Source Projects should follow the existing coding conventions in the code that you are working on. For example, if the bracket is on the same line as the if statement, then you should write all your code to have that convention.

If you commit code that does not follow these conventions and you are caught, you are responsible for also fixing your own code.

Below is a list of coding conventions that are specific to gvSIG, everything else not specificially mentioned here should follow the official Sun Java Coding Conventions

Why code conventions

As explained in the Sun Java Coding Conventions:

Code conventions are important to programmers for a number of reasons:

gvSIG specific coding conventions

Mandatory conventions

  1. Headers

    Look at the Headers document for more information.

  2. Indentations

    4 spaces. NO tabs.

  3. Javadoc

    All API interfaces must be fully documented through javadocs comments at interface, method and package level.

    When you inherit or extend from another interface or class which is already documented, and implement or rewrite one of the parent methods, don't write any javadoc comments, as they are also inherited since java 1.4.

  4. Brackets

    All brackets should begin at the end of the line that begins the statement, and end on a new line indented to the beginning of the statement. Example:

    AVOID:

    public class MyClass 
    {
    
        public void someMethod() 
        {
            if (...) { // Do something }
    }
    }
    

    RIGHT:

    public class MyClass {
    
        public void someMethod() {
            if (...) {
              // Do something
            }
        }
    }
    

    Brackets are mandatory even for single line statements:

    if (expression)       // AVOID!
        // some code
    
    if (expression) {     // RIGHT
        // some code
    }
    
  5. Class variables should not have any prefix or suffix related to its data type or scope. Example:

    String nameString;   // AVOID!
    
    String name;         // RIGHT
    
  6. Avoid lines longer than 80 characters for Code, comments, ...

  7. Logging

    Do not use System.out to log. Instead, use the SLF4J logging API. For example:

    private static final Logger LOG = LoggerFactory.getLogger(MyClass.class);
    
    public void someMethod() {
        LOG.debug("some debug text");
    }
    

    For more information on SLF4J usage, you can read the Logging section in this document.

  8. Exception handling

    Managing exceptions correctly requires experience. This is not supposed to be a guide on managing exceptions, simply a few best practices.

    • Rule 1: Try to catch exceptions as much as possible and rethrow higher level exceptions (meaning hiding the low level detailed and putting a message that is more related to the function of your code).
    • Rule 2: It is important not to loose the stack trace which contains important information. Use chained exceptions for that.
    • Rule 3: Always log the exception at the higher level (ie. where it is handled and not rethrown).
    • Rule 4: Create a few parent Exception for each API library, so methods of the API that throw any exception use that parent exception or one of the child ones.
    • Rule 5: If you have an exception or an error which can't be handled or resolved by code, throw or rethrow a BaseRuntimeException subclass.
    • Rule 6: At user interface level, catch all exception, log it, and informto the user of error in a user friend

    An example:

    public void getTestClass() {
        try {
            Class responseClass =
                Class.forName("some.package.MyClass");
        } catch (ClassNotFoundException cnfe) {
            String message = "Cannot instantiate test class";
            LOG.warn(message, ex);
            throw new ChainedRuntimeException(message, e);
        }
    }
    
  9. Qualified imports

    All import statements should containing the full class name of classes to import and should not use the "*" notation: An example:

    // AVOID!
    import java.util.*;
    import java.net.*;
    
    // RIGHT
    import java.util.Date;
    import java.net.HttpURLConnection;
    
  10. Use interfaces in the declaration of methods and variables.

    By example, if you need a variable x that is a list, declare it as List instead an ArrayList:

    // AVOID!
    ArrayList x = new ArrayList();
    HashMap y = new HashMap();
    
    public HashMap create(ArrayList keys, ArrarList values) {
        ...
    }
    
    // RIGHT
    List x = new ArrayList();
    Map y = new HashMap();
    
    public Map create(List keys, List values) {
        ...
    }
    
  11. API packages

    Usually, API interfaces and classes will belong to the library's main root package. If you create subpackages, use them to group by functionality, not by type.

  12. How to name packages

    All packages must begin with org.gvsig.
    Normally, the package is named org.gvsig followed, a package name that identifies the logical or functional block will contain.

  13. Uso de listeners de swing.

    Cuando estamos creando interfaces de usuario de swing, a la hora de atrapar los eventos de los componentes, se usaran clases anónimas y no se enlazaran los listeners a la clase en la que estén los componentes de swing. Así mismo, cuando el código del evento exceda de dos o tres lineas, se crearan métodos privados en la clase para gestionar esa acción especifica.
    // AVOID !!!!!
    public static class MyPanel extends JPanel implements ActionListener {
    
        private void initComponents() {
            JButton btnOk = new JButton();
            btnOk.addActionListener(this);
        }
    
        @Override
        public void actionPerformed(ActionEvent e) {
            ...
        }
    }
    
    // RIGHT
    public static class MyPanel extends JPanel {
    
        private void initComponents() {
            JButton btnOk = new JButton();
            btnOk.addActionListener(new ActionListener() {
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    btnOk_actionPerformed();
                }
            });
        }
    
        private void btnOk_actionPerformed() {
    
        }
    }
    


Nomenclatura para clases e interfaces

Uso de prefijos y sufijos en general

El uso de prefijos y/o sufijos en el nombre de clases e interfaces es normalmente una forma de aportar información sobre su naturaleza y/o cometido. Por ejemplo se pueden utilizar para denotar un patrón o un rol dentro de un patrón (por ejemplo el sufijo Factory). En otras ocasiones, se hace necesario su uso porque el nombre adecuado ya está asignado. Este es el caso de los prefijos y sufijos que se caracterizan a continuación.

Como regla general:

Uso del prefijo "Abstract"

Ejemplo:

public interface List {}
public abstract class AbstractList extends AbstractCollection implements List {}
Uso del prefijo "I"
Uso del prefijo "Default"

Ejemplo:

public interface ListModel {}
public abstract class AbstractListModel implements ListModel, Serializable {}
public class DefaultListModel extends AbstractListModel {}
Uso del prefijo "Base"

Ejemplo:

public abstract class StreamRequestHandler {}
public class BaseHTTPRequestHandler extends StreamRequestHandler {}
Uso del sufijo "Impl"

Ejemplo:

public interface Plane {}
public abstract class AbstractPlane implements Plane {}
 
// Ambos son correctos (pero no iguales)
public class PlaneImpl implements Plane {} 
public class PlaneImpl extends AbstractPlane {}
Condiciones de aplicación

Este criterio se aplicará en los desarrollos que se realicen bajo el ambito de la Asociacion gvSIG o derivado de contratos realizados por esta.

Nombres de paquetes a usar en gvSIG

Consideraciones previas

Debido a la dispersión y falta de unidad en los nombres de paquetes que se han venido utilizando en gvSIG 1.X, se decidio uniformizarlos, dando una identidad de proyecto por encima del de la empresa que realiza el desarrollo.

Criterio a seguir

A la hora de crear paquetes java que deban formar parte de una distribución oficial de gvSIG o vayan a llevar el respaldo oficial del proyecto gvSIG, estos colgarán del paquete:

org.gvsig

No haciendo mención en el paquete a la empresa que realiza el desarrollo.

Normalmente, el paquete se nombrará org.gvsig seguido, de un nombre de paquete que identifique el bloque lógico o funcional que va a contener.

Condiciones de aplicación

Este criterio se aplicará en los desarrollos que se realicen bajo el ambito de la Asociacion gvSIG o derivado de contratos realizados por esta.

Logging

En gvSIG se emplea la librería SLF4J como API de logging a emplear.

SLF4J Simple Logging Facade o "Fachada de Registro Simple" es un framework que se ha creado para abstraer el sistema de registro que hay por debajo. Como el sistema de registro más popular usado es Log4j, el API del framework es muy similar para simplificar al máximo eliminar la dependencia directa de este sistema. Este framework por tanto es una capa de abstracción del sistema de registro, y nos va a permitir cambiar el componente de registro que lleve por debajo sin necesidad de un gran esfuerzo de recodificación de la aplicación.

Características de SLF4J
// Those lines produce the same results. The second one has parameters that reduce the overhead when debuggin is disabled
LOG.debug("The new entry is "+entry+"."); 
LOG.debug("The new entry is {}.", entry);
Cómo usar SLF4J en gvSIG

gvSIG 2.0 ya está preparado para trabajar con SLF4J, usando LOG4J como implementación, por lo que podemos usarlo directamente desde cualquier extensión de gvSIG.

Para usar SLF4J desde una clase Java basta con incluir las siguientes sentencias:

1.- Importar las clases necesarias

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

2.- Declaramos e inicializamos el Logger.

public class MyClass
{
    private static final Logger LOG = LoggerFactory.getLogger(MyClass.class);
    ...

3.- Usamos el Logger dentro del código de la clase, cuando queramos mostrar un mensaje de log, dependiendo del tipo: error, alerta, información, depuración o traza:

LOG.warn(String message);
LOG.warn(String message,Object arg1);
LOG.warn(String message,Object[] arg1);
LOG.warn(String message, Throwable arg1);
LOG.info(String message);
LOG.info(String message,Object arg1);
LOG.info(String message,Object[] arg1);
LOG.info(String message, Throwable arg1);
LOG.error(String message);
LOG.error(String message,Object arg1);
LOG.error(String message,Object[] arg1);
LOG.error(String message, Throwable arg1);
LOG.debug(String message);
LOG.debug(String message,Object arg1);
LOG.debug(String message,Object[] arg1);
LOG.debug(String message, Throwable arg1);
LOG.trace(String message);
LOG.trace(String message,Object arg1);
LOG.trace(String message,Object[] arg1);
LOG.trace(String message, Throwable arg1);

Los siguientes métodos se proporcionan para consultar la activación de los mensajes por nivel:

LOG.isErrorEnabled();
LOG.isWarnEnabled();
LOG.isInfoEnabled();
LOG.isDebugEnabled();
LOG.isTraceEnabled();

En SLF4J tenemos la capacidad adicional de parametrizar los mensajes, insertando variables dentro de la cadena String del mensaje de registro, como en el siguiente ejemplo:

private Integer temperature;

public void setTemperature(Integer temperature) {

    LOG.debug("Setting temperature to {}. Old temperature was {}.", temperature, this.temperature);

    this.temperature = temperature;

    if(temperature.intValue() > 50) {
      LOG.info("Temperature has risen above 50 degrees.");
    }
}

Esto evita que tengamos que ir concatenando Strings, lo cuál reduce el coste del uso de las instrucciones de logging. Esto es debido a que, aunque el nivel de log que estemos usando esté desactivado (por ejemplo, el de debug), la invocación al método se hará de todas formas, incluyendo la concatenación de Strings para construir el mensaje, si la hubiera.

De todas formas, si la obtención de alguno de los parámetros que vamos a pasar al mensaje de log fuera costosa, es conveniente emplear los métodos de consulta para evitar dicha ejecución. Por ejemplo:

private Integer temperature;

public void setTemperature(Integer temperature) {

    LOG.debug("Setting temperature to {}. Old temperature was {}.", temperature, this.temperature);

    this.temperature = temperature;
    addToTemperatureLog(temperature);

    if (LOG.isDebugEnabled()) {
        LOG.debug("The current average temperature is {} Celsius", calculateAverageTemperature());
    }
}

Interfaces de usuarios

Consideraciones generales
  1. Las herramientas deben denominarse con la misma cadena en cualquier lugar donde aparezcan, tanto en la barra de herramientas como en la barra de menús, etc. Además de dar soporte para ser traducida.
  2. Cada herramienta se debe identificar siempre con el mismo icono. No es posible que tenga un icono en el menú y otro distinto en la barra de herramientas, por ejemplo.
  3. Las ventanas deben incluir siempre un título, el cuál debe ser conciso y con soporte para ser traducido.
Cadenas de texto.
  1. Evitar el uso de abreviaturas cuando no existan problemas de espacio.
  2. Evitar textos muy largos que den información redundante, facilitando la reutilización de claves de traducción. Ejemplo: “Seleccione el tamaño que desea:” por “Tamaño”.
  3. Se debe consultar en "i18n/translations.all" en la instalación de gvSIG las traducciones ya existentes en gvSIG para comprobar si ya existe una cadena igual a la que queremos presentar y deberíamos reutilizar su clave. En este caso, deberemos seguir incluyendo en los properties de nuestro proyecto dicha entrada con la misma traducción en castellano e inglés a la que hay en "i18n/translations.all".
  4. Todos los textos de una ventana deben estar traducidos al castellano y al inglés como mínimo, evitando que aparezcan claves de traducción en vez del texto traducido, o que aparezcan textos en inglés en la interfaz en castellano.
  5. Prestar atención a fallos ortográficos, sobretodo en los acentos a la hora de crear las traducciones.
  6. En cualquier referencia a los documentos de gvSIG, como por ejemplo “Vista”, la primera letra debe ir en mayúsculas.
  7. Hay que seguir siempre la misma pauta en las mayúsculas y minúsculas cuando se presenta el caso de un desplegable con distintos elementos. Siempre deben empezar todos por el mismo tipo de letra.
  8. Usaremos "inglés" en la claves de traduccion. No las abreviaremos reflajando el mismo texto en la clave que en la traduccion al inglés. Reflejaran el texto que debe aparecer en la traduccion usando "_" en lugar de espacios en blanco.
  9. Todas las claves de traduccion que se creen nuevas deberan comenzar por "_".
  10. Cuando una cadena de traduccion precise parametros, en la clave se introduciran estos en la forma "XnumeroX" en el sitio correspondiente donde "numero" empieza en cero e identificara al parametro. En la traduccion, se sustituira "XnumeroX" por "{numero}" en el sitio que corresponda.
  11. Cuando en una cadena de traduccion aparezcan caracteres de puntuacion usar las siguientes reglas para la clave de traduccion:
XcolonX
":"
XsemicolonX
";"
XdotX
"."
XnlX
"\n"
XellipsisX
"..."
XquestionX
"?"
XexclamationX "!"
Dimensiones de cuadros de dialogo y paneles en general
  1. Los componentes de los paneles deberan ajustarse al tamaño del panel, presentando este un tamaño por defecto adecuado para mostrar los elementos que contiene.
  2. Evitar que aparezcan textos cortados e ilegibles obligando a que el usuario tenga que volver a dimensionar la ventana cada vez que acceda a ella. Hay que tener en cuenta que otras traducciones pueden ocupar mas que el texto en castellano o inglés.
  3. Prestar atención a los espacios sobrantes en las ventanas, sobretodo en la parte inferior.
  4. Intentar homogeneizar una ventana que incluya varios paneles, de manera que no existan espacios sobrantes exagerados.
  5. Dejar márgenes de 5 píxeles entre paneles interiores y con los bordes de la ventana.
Formularios
  1. Organizar los campos de un formulario en una sola columna de datos.
  2. Todas las opciones usadas para un grupo de componentes JRadioButton deben ser excluyentes.
  3. Los textos de los elementos JLabel que precedan a un JTextField, JComboBox, etc. en un formulario deben terminar con los dos puntos “:”.
  4. Cuando un componente con texto se encuentra en estado enabled=false, el texto debe aparecer en gris.
  5. La separación entre un componente JLabel y el componente de su derecha no debe ser inferior a 5 píxeles de manera que sea legible el texto. Además el tamaño máximo de separación no debe ser generalmente, mayor que el tamaño del propio texto del componente JLabel.
  6.  No se deben incluir botones sin funcionalidad.
  7.  El texto de los botones que aparecen en varias ventanas debe ser el mismo siempre. Por ejemplo, el botón con texto “Aceptar” a veces aparece con el texto “Ok”, cuando siempre debe poner “Aceptar”.
  8.  Los botones de Aplicar/Aceptar/Cancelar deben aparecer siempre en este orden y en la esquina inferior derecha de la ventana (en la misma alineación horizontal).
Creación de interfaces de usuario

A la hora de crear interfaces de usuario, siempre que sea posible, se utilizará la herramienta abeille. Esta herramienta es adecuada para la creación de interfaces de usuario de tipo formulario. Si el interface de usuario a crear no es un formulario, no es preciso usar esta herramienta.

[ http://devel.gvsig.org/download/runtimes/abeille/ ]

A la hora de crear los interfaces de usuario estos se guardarán en formato xml junto a los ficheros fuente del proyecto. Además se generará código java asociado al interface de usuario usando el sufijo "View" para nombrar la clase java a generar. Estos ficheros java generados nunca deberán ser editados. Para cada panel se crearán dos o tres clases java. Vamos a ver esto con un ejemplo. Supongamos que estamos creando el panel "general"  de propiedades de una capa. Tendremos:

A la hora de crear el interface de usuario deberemos de tener en cuenta:

A la hora de crear la clase "controller", tendremos en cuenta:

Estas consideraciones estan relacionadas con la implementación de paneles y cuadros de diálogo. Normalmente estos llevaran asociada en el API un interface definiendo el API del panel, que deberá estar implememtado con la clase "controller". Desde fuera de la implementación no se deberá acceder nunca a los componentes del interface de usuario, debiendo existir métodos para asignar los valores de entrada del formulario y recoger los de salida. Si estos métodos se limitan a hacer get y set sobre los componentes del formulario, revisa el análisis, es fácil que hayas trasladado al cliente del panel responsabilidades del "controller".