Plugins y extensiones
Plugins y extensiones. Definición, archivos implicados, clases...
Plugins
Plugins y extensiones
En un gvSIG instalado, existe un subdirectorio llamado gvSIG/extensiones. Dicho subdirectorio contiene a su vez un conjunto de directorios, cada uno de los cuales constituye un plugin. Los plugins no son más que un conjunto de clases Java que añaden nuevas funcionalidades a la aplicación. Si inspeccionamos el mencionado directorio observaremos que existe un plugin llamado com.iver.core, que contiene libCorePlugin (el skin de Andami) y un plugin llamado com.iver.cit.gvsig, que contiene el plugin gvSIG (el plugin principal de la aplicación). Dependiendo de la instalación de gvSIG que hayamos realizado, podremos localizar otros plugins como com.iver.cit.gvsig.cad (funcionalidades de edición de cartografía), org.gvsig.scripting (que añade las capacidades de scripting), com.iver.cit.gvsig.wms (cliente de WMS), etc
Las nuevas funcionalidades añadidas por los plugins, deben conectarse de alguna forma con el resto de la aplicación. Este punto de conexión lo aportan las extensiones, que son clases Java que implementan la interfaz IExtension, y que hacen de puente entre la funcionalidad ya existente y la nueva funcionalidad aportada por el plugin.
Estructura de un plugin
El contenido mínimo que un plugin debe incluir se reduce a un fichero XML llamado config.xml. Este fichero contiene el nombre del plugin y un abanico de detalles relevantes sobre el mismo: el directorio que contendrá las librerías aportadas por el plugin, las dependencias que tiene con otros plugins, el nombre base de los ficheros de traducciones, las extensiones que aporta, y una descripción de los componentes que deben añadirse a la interfaz de usuario (menús, barras de herramientas, etc).
Dependiendo de los elementos definidos en el config.xml, el plugin puede incluir también: uno o varios ficheros JAR en el directorio de librerías, varios ficheros de traducciones, imágenes o cualquier otro tipo de datos (cartografía, datos de conexión a algún servicio, paletas de colores, etc)
Podemos ver la estructura del plugin WFS para hacernos una idea de una distribución típica de ficheros:
gvSIG/ extensiones/ com.iver.cit.gvsig.wfs2/ build.number config.xml text_en.properties text_fr.properties \.\. text.properites lib/ com.iver.cit.gvsig.wfs2.jar images/ backward.png down-arrow.png fastbackward.png fastforward.png
Normalmente, las librerías se depositan en el directorio raíz del plugin, o en un subdirectorio lib/. Las imágenes suelen situarse en un subdirectorio images. Sin embargo, la estructura puede alterarse a nuestro antojo, siempre que en el código usemos la ruta relativa adecuada (en el caso de las imágenes) o definamos correctamente el directorio en el config.xml (en el caso de las librerías).
El fichero config.xml
La filosofía en la que se apoya la extensibilidad de Andami es la siguiente: existen unas clases especiales, llamadas extensiones, que implementan la interfaz IExtension, la cual incluye un método execute() (entre otros). En el fichero config.xml del plugin, se definen una serie de elementos de interfaz de usuario que se van a añadir a la aplicación (herramientas, menús y controles de la barra de estado), y se asocia cada uno de estos elementos con una extensión. En concreto, cuando el usuario pinche en una herramienta o una entrada de menú, Andami ejecutará el método execute de la extensión asociada. De esta forma, la extensión realizará las acciones necesarias para ejecutar la operación solicitada, posiblemente apoyándose en otras clases o librerías del plugin.
Vemos por tanto que la extensión es el punto de acceso a las nuevas funcionalidades aportadas por el plugin, y que los elementos de interfaz de usuario (definidos en el config.xml) junto con la extensión asociada constituyen el nexo entre el resto de la aplicación y el plugin.
Ejemplo de fichero config.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<plugin-config>
<depends plugin-name="com.iver.cit.gvsig" />
<libraries library-dir="./lib"/>
<resourceBundle name="text"/>
<extensions>
<extension class-name="com.iver.cit.gvsig.wfs.WFSClientExtension"
description="Support to access WFS"
active="true"
priority="1">
</extension>
<extension
class-name="com.iver.gvsig.centerviewpoint.CenterViewToPointExtension"
description="Haces zoom a partir de un par de coordenadas"
active="true">
<menu text="Vista/Centrar_la_Vista_sobre_un_punto"
tooltip="Centrar_la_Vista_sobre_un_punto"
action-command="CENTERVIEWTOPOINT"
icon="images/centerviewtopoint.png" />
<tool-bar name="com.iver.cit.gvsig.Herramientas">
<action-tool icon="images/centerviewtopoint.png"
action-command="CENTERVIEWTOPOINT"
tooltip="Centrar_la_Vista_sobre_un_punto"
enable-text="debería de estar activada" last="true"/>
</tool-bar>
</extension>
</extensions>
</plugin-config>
El fichero utiliza una sintaxis XML, que básicamente implica que tiene una estructura marcada por etiquetas que deben abrirse y posteriormente cerrarse en orden inverso al orden de apertura. Se puede encontrar más información sobre XML en el sitio web del W3 Consortium.
Etiquetas permitidas
A continuación, se describe en detalle cada una de las etiquetas permitidas. No obstante, dentro de la distribución del código fuente de gvSIG, existe un fichero llamado plugin-config.xsd, situado dentro de _fwAndami/schemas, donde se describen formalmente todas las etiquetas permitidas y el tipo de valores que aceptan. Este fichero está escrito en lenguaje XSD Schema, un lenguaje de descripción de sintaxis XML. Ese fichero debe constituir la referencia principal a la hora de conocer las etiquetas permitidas.
![Jerarquía de etiquetas válidas de plugin-config.xml](../andami.img/plugin-config.png)
Jerarquía de etiquetas válidas de plugin-config.xml
Cabecera
El fichero XML debe ir encabezado por una cabecera como la siguiente:
<?xml version="1.0" encoding="UTF-8"?>
Es importante que el primer carácter < sea el primero del fichero (es decir, que no haya ningún carácter en blanco antes de la cabecera), de lo contrario el fichero se leerá incorrectamente.
- version: Indica la versión de estándar XML que vamos a usar (en nuestro caso, "1.0")
- encoding: Indica la codificación de caracteres con la que vamos a salvar el fichero en disco. La codificación que especifiquemos aquí debe coincidir con la codificación que realmente usemos en el momento de salvar el fichero (los editores de texto normalmente permiten especificar la codificación en algún apartado). Algunas codificaciones típicas son "UTF-8", "ISO-8859-15" e "ISO-8859-1"
<plugin-config>
Etiqueta que simplemente marca el inicio y el fin del fichero de configuración; es la raíz del árbol XML.
<resourceBundle>
Indica el nombre base que tendrán los ficheros de traducción. Por ejemplo, si escribimos <resourceBundle name="text"/>, los ficheros de traducciones se llamarán text.properties, text_en.properties, etc.
<depends>
Indica los plugins de los que va a depender nuestro plugins, a nivel de librerías. Esto significa que si deseamos usar algunas clases o librerías que estén en un plugin distinto, debemos declarar una dependencia de ese plugin. Debe haber una entrada <depends> por cada plugin del que dependamos.
Las dependencias determinan también el orden de carga de plugins, de forma que un plugin siempre se carga después de que sus dependencias han sido ya cargadas. Normalmente, todos los plugins dependen del plugin gvSIG (com.iver.cit.gvsig), por lo que este plugin se carga el primero.
<libraries>
Establece el directorio del que se leerán las librerías de nuestro plugin. Normalmente suele usarse <libraries library-dir="."> (para indicar el directorio raíz del plugin), o bien <libraries library-dir="lib"> (para indicar el subdirectorio lib). Dos plugins distintos pueden tener versiones distintas de una misma librería, y ambas funcionarán correctamente sin interferencias, salvo en los siguientes supuestos:
- Si un plugin A declara una dependencia de plugin B, y ambos poseen la misma librería pero en diferente versión: el plugin B usará su propia versión sin ningún problema; el plugin A usará su propia versión si el plugin B se carga después de A, en caso contrario se usará la versión de B.
- Si existe un plugin A que posee una librería, y esta librería está también en el CLASSPATH inicial de Andami (declarado al lanzar Andami), el plugin A usará la versión presente en el CLASSPATH inicial.
<label-set>
Indica el comienzo de un grupo de etiquetas (label) para la barra de estado. Cada grupo de etiquetas (label-set) está asociado a una clase, y sólo es visible cuando la ventana visible es instancia de dicha clase. Acepta los siguientes atributos:
- class-name Nombre de la clase asociada a este label-set. Las etiquetas del label-set sólo serán visibles cuando la ventana activa sea instancia de esta clase.
<label>
Instala una etiqueta (una caja de texto no editable) en la barra de estada. La etiqueta siempre pertenece a un grupo de etiquetas (label-set) y sólo será visible cuando lo sea el label-set. Acepta los siguientes atributos:
- id Identificador (nombre) de la etiqueta. Se usará para acceder a la etiqueta y escribir sobre ella.
- size Anchura de la etiqueta (en píxeles).
<extensions>
Marca el inicio de la lista de extensiones.
<extension>
Declara una extensión de Andami. Principalmente, la declaración incluye el nombre de la clase Java que implementa esta extensión, y una lista de elementos de interfaz de usuario que deberán añadirse a la aplicación y estarán asociados con esta extensión. <extension> acepta los siguientes atributos:
- class-name Nombre de la clase que implementa esta extensión. La clase debe estar localizada en alguna de las librerías incluidas en el plugin.
- description Descripción de la funcionalidad aportada por la extensión.
- active Establece si la extensión va a estar activa o no. Si no está activa, es como si no existiese.
- priority La prioridad de la extensión determina el orden de carga respecto a otras extensiones del mismo plugin. Una extensión con una prioridad menor se cargará antes que otra extensión con una prioridad mayor. Hay que tener en cuenta que las dependencias entre plugin son las que determinan el orden de carga de plugins, por tanto si lo que queremos es cambiar el orden de carga de dos extensiones que están en plugins distintos, debemos modificar las dependencias entre ellos, no la prioridad. Por convención, se usan valores entre 1 y 99999 (aunque realmente se admiten rangos mayores). Para una extensión sin necesidades especiales, podemos omitir el valor prioridad, o darle un valor de 2000.
<tool-bar> Crea una barra de herramientas (grupo de herramientas) con el nombre dado. Dentro esta etiqueta se anidarán los botones que deseamos añadir a esta barra de herramientas. Diferentes extensiones pueden añadir botones a la misma barra de herramientas, basta con que se llamen de la misma forma. <tool-bar> acepta los siguientes atributos:
- name Nombre de la barra de herramientas. Sirve para agrupar botones de extensiones diferentes(si el nombre de la tool-bar es el mismo, se añadirán a la misma tool-bar. También se mostrará en la lista de barras de herramientas, traducido al idioma correspondiente, por lo que name debe considerarse como una clave de traducción.
- position Sirve para establecer la posición de la barra de herramientas respecto a otras. Una tool-bar con un valor de position más bajo que la de otra tool-bar indica que la primera tool-bar se situará más a la izquierda que la segunda. Si diferentes extensiones definen diferentes positions para la misma barra de herramientas, sólo se tendrá en cuenta la position definida en la extensión de mayor prioridad (la que tenga un valor de priority más bajo).
- is-visible Determina si la tool-bar estará inicialmente visible u oculta. Esto puede cambiarse posteriormente por código, o bien por el usuario desde los menús.
<action-tool>
Una action-tool es una herramienta (un botón) que lanza una acción concreta. El mecanismo exacto es el siguiente: el usuario pulsa el botón, Andami recibe el evento de pulsado, consulta la extensión asociada a este botón y lanza el método execute(String actionCommand) de dicha extensión, pasándole como parámetro el comando (action-command) definido para este botón. action-tool acepta los siguientes atributos:
- action-command Especifica el comando que deseamos que sea ejecutado por la extensión asociada. Se puede omitir si la extensión va a tener un solo botón (o menú, etc) asociado, o bien si todos los botones (menús, etc) realizarán la misma acción.
- name Nombre del botón. Útil si posteriormente deseamos obtener el botón para cambiarle el icono, por ejemplo.
- position Sirve para establecer la posición del botón dentro de la barra de herramientas. Un botón con un valor position más bajo que la de otro botón se situará más a la izquierda que el segundo botón. Si diferentes botones poseen la misma posición o no poseen posición, se ordenarán arbitrariamente. Un botón que posea posición siempre irá a la izquierda de un botón sin posición. No obstante, no se recomienda omitir este atributo.
- icon Icono que se mostrará dentro del botón. La ruta de la imagen debe ser relativa al directorio raíz del plugin.
- text Texto (opcional) a mostrar junto al icono.
- tooltip Texto que se mostrará en el globo de texto (tooltip) que aparece al mantener quieto el ratón sobre el botón durante unos segundos.
- enable-text Texto que informa al usuario de las condiciones que deben producirse para que este botón esté activo. Cuando el botón está visible pero desactivado, el tooltip muestra este texto en vez del normal, para que el usuario sepa qué debe hacer para usar esta herramienta.
- last Indica que deseamos dejar un espacio extra a la derecha de este icono. Normalmente se usa en el último icono de una barra de herramientas, para marcar más la separación con la barra de herramientas contigua.
<selectable-tool>
Una selectable-tool es un botón que puede estar pulsado (herramienta seleccionada) o no pulsado (herramienta no seleccionada), y no ejecuta una acción como la action-tool, sino que indica un cambio de estado (por ejemplo, "Zoom Más seleccionado", "Zoom Menos seleccionado", etc). Cuando una selectable-tool está pulsada, las demás están no-pulsadas. Si pulsamos en otra selectable-tool, la anterior deja de estar pulsada. La selectable-tool seleccionada en cada momento va asociada a la ventana activa: si cambiamos de ventana activa, se carga la herramienta seleccionada asociada a la nueva ventana activa. Si volvemos a la ventana anterior, se vuelve a seleccionar la herramienta asociada a la ventana anterior.
![Detalle de dos herramientas seleccionables (la izquierda está seleccionada)](../andami.img/selectable-tools.png)
Detalle de dos herramientas seleccionables (la izquierda está seleccionada)
La etiqueta selectable-tool admite los mismos atributos que action-tool, casi todos con la misma semántica, salvo action-command que en este caso indica la herramienta seleccionada. Adicionalmente, selectable-tool permite:
- is-default Determina si esta herramienta estará seleccionada inicialmente.
<menu> Crea una entrada de menú. El menú funcionará a todos los efectos como una action-tool, es decir, al pinchar en el menú se lanzará el método execute() de la extensión asociada, pasándole como parámetro el action-command especificado por el menú. Admite los siguientes atributos:
- action-command Especifica el comando que deseamos que sea ejecutado por la extensión asociada. Se puede omitir si la extensión va a tener un solo botón (o menú, etc) asociado, o bien si todos los botones (menús, etc) realizarán la misma acción.
- text Determina la localización y el texto a mostrar por la entrada del menú. text debe tener la forma "Archivo/Abrir". Cada porción separada por barras (/) indica un contenedor de menú, como Archivo, excepto la última porción que indica el texto a mostrar en la entrada misma. Por ejemplo, "Archivo/Abrir" creará una entrada llamada Abrir dentro del menú Archivo. Otro ejemplo: "Archivo/Plantillas/Abrir Plantillas" creará una entrada Abrir Plantillas dentro del submenú Plantillas, dentro del menú Archivo. Si los menús contenedores no existiesen, se crearían automáticamente. Hay que tener en cuenta que el texto real mostrado en los menús y submenús se traduce al idioma seleccionado por el usuario, por lo que cada porción de texto empleado aquí constituye una clave de traducción.
- position Sirve para establecer la posición de la entrada de menú dentro del submenú en el que esté situada. Una entrada con un valor position más bajo que la de otra entrada se situará más arriba (más cerca del inicio del menú) que la segunda entrada. Si diferentes entradas poseen la misma posición, o no poseen posición, se ordenarán arbitrariamente. Una entrada que posea posición siempre estará más arriba que una entrada sin posición. No obstante, no se recomienda omitir este atributo.
- icon Icono que se mostrará dentro del botón. La ruta de la imagen debe ser relativa al directorio raíz del plugin.
- tooltip Texto que se mostrará en el globo de texto (tooltip) que aparece al mantener quieto el ratón sobre el botón durante unos segundos.
- enable-text Texto que informa al usuario de las condiciones que deben producirse para que este botón esté activo. Cuando el botón está visible pero desactivado, el tooltip muestra este texto en vez del normal, para que el usuario sepa qué debe hacer para usar esta herramienta.
- key La key es el carácter que forma la combinación de teclas necesaria para lanzar este menú. La combinación se forma usando el modificador de ese sistema operativo + key. Por ejemplo, en Windows el modificador es la tecla ALT, y la key de la entrada de menú Añadir capa es O, así que pulsando ALT+O nos aparece el diálogo de añadir capa.
- mnemonic El mnemonic es la tecla que activará esta entrada de menú cuando el menú esté desplegado y tenga el foco. Por ejemplo, cuando el menú Archivo está seleccionado, y pulsamos la tecla A, aparece el diálogo de abrir proyecto porque A es el mnemotécnico de la entrada Abrir proyecto.
- is_separator Atributo que indica que deseamos añadir un separador en esta posición de la barra de menús. Es un atributo especial que debe añadirse en una entrada de menú aparte, ya que no añade un separador debajo de la entrada actual, sino que transforma la entrada actual en un separador (y por tanto no se mostrará el texto ni el icono, ni se podrá pulsar sobre ella).
<combo-scale> Añade a la barra de estado un control de tipo combo box (control que permite elegir un valor de una lista de valores, o bien escribir el valor deseado), con una etiqueta de texto a la izquierda describiendo su función. Acepta los siguientes atributos:
- name Nombre del control. Debería ser diferente al nombre de cualquier otro control.
- label El texto que será mostrado a la izquierda del combo box.
- elements Una lista de valores numéricos separados por punto y coma. Cada valor constituirá una entrada del combo box.
- value Valor que estará inicialmente seleccionado. Si se omite, el primer elemento estará seleccionado.
- action-command Especifica el comando que deseamos que sea ejecutado por la extensión asociada. Se puede omitir si la extensión va a tener un solo botón (o menú, etc) asociado, o bien si todos los botones (menús, etc) realizarán la misma acción.
![Detalle de un combo-scale](../andami.img/combo-scale.png)
Detalle de un combo-scale
Extensiones
La interfaz IExtension
Como ya hemos explicado, las extensiones son clases que sirven de puente entre la funcionalidad básica de gvSIG y las funcionalidades aportadas por nuestro plugin. La extensión recibirá notificación de las acciones realizadas por el usuario, y ejecutará el código apropiado en respuesta a esas acciones (posiblemente usando otras clases de nuestro plugin).
El siguiente diagrama muestra de forma esquemática todas las clases que vamos a explicar en este capítulo y sus relaciones:
![Diagrama de las clases implicadas en la creación de extensiones](../andami.img/class-diagram-extensions.png/index.html)
Diagrama de las clases implicadas en la creación de extensiones
Todas las extensiones implementan la interfaz IExtension, que incluye los siguientes métodos:
- public void initialize() Una vez se leen y ordenan todas las extensiones presentes en los plugins, se llama a este método para cada una de ellas, de forma secuencial siguiendo el orden definido en la prioridad de las extensiones.
- public void postInitialize() Una vez se han inicializado todas las extensiones, se llama a este método para cada una de ellas, siguiendo el mismo orden de antes.
- public void terminate() Al salir de la aplicación, se llama a este método para cada una de las extensiones, siguiendo un orden secuencial inverso al que se siguió en la inicialización (es decir, la última extensión que se inicializó es la primera que se finalizará, y la primera extensión que se inicializó será la última en ser finalizada).
- public void execute(String actionCommand) En el config.xml se definen elementos de la interfaz de usuario (botones, menús, etc) que están asociados a una extensión. Cuando el usuario pulsa en uno de estos elementos de la interfaz, se busca la extensión asociada y se llama a este método. Los botones, menús, etc pueden tener asociado una cadena que actúa como identificador de comando (action command). Cuando se llama al método execute, se pasa como parámetro el actionCommand asociado al botón que se pulsó. De esta forma, si tenemos varios botones, menús, etc asociados con esta extensión, podemos discriminarlos comprobando el actionCommand que recibimos. Normalmente, será en este método donde escribiremos las principales acciones de nuestra extensión (abrir un asistente, comenzar algún procesamiento, etc).
Los dos siguientes métodos determinan si la extensión (y sus elementos de interfaz de usuario asociados) serán visibles y estarán activos. Estas propiedades se comprueban para todas las extensiones cada vez que se cambia de ventana activa y también justo después de procesar un evento de interfaz de usuario (es decir, después de llamar al método execute de una extensión).
- public boolean isEnabled() Los elementos de interfaz de usuario (botones, menús, controles de la barra de estado) asociados a esta extensión estarán activados (o desactivados) dependiendo del valor devuelto por este método. Cuando un botón está desactivado, aparece en gris claro y no es posible pulsar sobre él.
![Detalle de un menú activo y otro desactivado.](andami.img/menu-activ-deactivado.png)
Detalle de un menú activo y otro desactivado.
- public boolean isVisible() Los elementos de interfaz de usuario (botones, menús, controles de la barra de estado) asociados a esta extensión estarán visibles (o invisibles) dependiendo del valor devuelto por este método. Cuando un botón está invisible, no se mostrará independientemente del valor de isEnabled().
Los dos métodos siguientes sólo se usan con el mecanismo ExclusiveUIExtension, que permite a una extensión tomar el control de la interfaz y activar o desactivar (mostrar u ocultar) otras extensiones. Esto puede usarse para crear extensiones que personalicen gvSIG (por ejemplo, una extensión que transforme gvSIG en un simple visor de cartografía):
- public boolean isEnabled(IExtension extension) Decide si la extensión pasada como parámetro estará activada o no, ignorando el valor del método isEnabled de la propia extensión.
- public boolean isVisible(IExtension extension) Decide si la extensión pasada como parámetro estará visible o no, ignorando el valor del método isVisible de la propia extensión.
La clase abstracta Extension
Salvo que tengamos una necesidad muy especial, normalmente no implementaremos directamente la interfaz IExtension, sino que extenderemos la clase abstracta Extension, que a su vez ya implementa IExtension. Esto nos ofrece una cierta tranquilidad de futuro en el funcionamiento de nuestra extensión, ya que si se añadiese algún método a la interfaz IExtension, este método se implementaría en la clase Extension de forma que nuestra extensión seguiría funcionando sin cambios (a no ser que necesitase un comportamiento para el nuevo método distinto del implementado en Extension).
HiddableExtension y ExtensionDecorator
La interfaz HiddableExtension y la clase ExtensionDecorator permiten cambiar la visibilidad de una extensión (y sus controles asociados) en tiempo de ejecución, ignorando el valor que indique la propia extensión en el método isVisible. De esta forma podemos instalar una extensión que oculte un conjunto de otras extensiones, creando un gvSIG personalizado (que sólo mostraría las extensiones deseadas).
La interfaz HiddableExtension sólo contiene dos métodos:
- public void setVisibility(int state) Permite definir una visibilidad para una extensión, que prevalece sobre la visibilidad definida en el método isVisible() de la propia extensión. Los valores de visibilidad aceptados incluyen: ExtensionDecorator.INACTIVE (el Decorator está desactivado y por tanto se obedecerá a la establecido en el método isVisible() de la propia extensión), ExtensionDecorator.ALWAYS_VISIBLE (la extensión estará siempre visible) y ExtensionDecorator.ALWAYS_INVISIBLE (la extensión estará siempre invisible).
- public int getVisibility() Consulta la visibilidad que se ha adjudicado a la extensión. Los valores devueltos pueden ser ExtensionDecorator.INACTIVE, ExtensionDecorator.ALWAYS_VISIBLE o ExtensionDecorator.ALWAYS_INVISIBLE.
La clase ExtensionDecorator implementa la interfaz HiddableExtension. Cada extensión cargada por Andami posee un ExtensionDecorator asociado, que podemos obtener para cambiar la visibilidad de esa extensión. Mostramos como ejemplo el código necesario para ocultar la extensión "com.iver.cit.gvsig.StartEditing", que es la que permite poner una capa en edición. De esta forma, estaríamos desactivando las capacidades de edición de gvSIG:
ExtensionDecorator decorator = PluginServices.getDecoratedExtension(com.iver.cit.gvsig.StartEditing.class);
decorator.setVisibility(ExtensionDecorator.ALWAYS_INVISIBLE);
Por supuesto, para llevar a cabo con éxito una personalización de gvSIG, necesitamos conocer las extensiones que deseamos ocultar. Además, para poder acceder correctamente a la clase de la extensión a desactivar (en este ejemplo, com.iver.cit.gvsig.StartEditing.class) necesitamos declarar una dependencia del plugin que contenga la extensión, de lo contrario obtendremos una excepción de tipo ClassNotFoundException al ejecutar ese código. En este caso, com.iver.cit.gvsig.StartEditing.class está contenido en el plugin com.iver.cit.gvsig.cad, por tanto necesitamos añadir al config.xml de nuestro plugin una línea como la siguiente:
<depends plugin-name="com.iver.cit.gvsig.cad" />
Téngase en cuenta que cualquier extensión puede usar este mecanismo en cualquier momento para ocultar o mostrar otra extensión. Para evitar comportamientos impredecibles, ExtensionDecorator sólo debería usarse en extensiones especiales que deseen personalizar gvSIG, y no deberían instalarse dos extensiones de este tipo al mismo tiempo (de lo contrario, es posible que lo que una de ellas vaya ocultando, la otra lo vaya mostrando y será difícil prever el resultado final, salvo que ambas extensiones realicen estas ocultaciones de forma coordinada).
El mecanismo ExclusiveUIExtension
El mecanismo ExclusiveUIExtension permite realizar algo similar a lo que se consigue con ExtensionDecorator, pero en este caso existe una única extensión que decidirá el estado de las demás. Es decir, con ExtensionDecorator cualquier extensión puede modificar el estado de las demás. Con ExclusiveUIExtension, se definirá una única extensión que tendrá la capacidad de mostrar/ocultar y activar/desactivar el resto de extensiones.
En cualquier caso, ambos mecanismos pueden usarse al mismo tiempo y ExclusiveUIExtension respetará lo definido por ExtensionDecorator, de forma que si ExtensionDecorator define una visibilidad ALWAYS_VISIBLE (siempre visible), la extensión estará visible pese a que ExclusiveUIExtension defina la extensión como oculta. Es decir, ExclusiveUIExtension sólo actuará en caso de que el ExtensionDecorator esté inactivo (modo ExtensionDecorator.INACTIVE). En cualquier caso, ExtensionDecorator sólo actúa sobre la visibilidad de la extensión, mientras que el mecanismo ExclusiveUIExtension puede definir también si la extensión está activa (enabled) o no.
Para usar ExclusiveUIExtension necesitamos:
- Tener una extensión que implemente correctamente los métodos isEnabled(IExtension extension) y isVisible(IExtension extension). Esta extensión actuará como ExclusiveUIExtension, decidiendo el estado (visible/invisible, activa/desactivada) del resto de extensiones.
- Registrarse como ExclusiveUIExtension en Andami, dentro del método initialize de nuestra extensión, utilizando el método PluginServices.setExclusiveUIExtension().
- Otra opción equivalente a registrarnos utilizando el método setExclusiveUIExtension consiste en sumistrar un parámetro de arranque a gvSIG. El parámetro es el siguiente: ExclusiveUIExtension=NombreDeExtension, donde NombreDeExtension debe sustituirse por el nombre de la extensión mencionada en el punto anterior.
- En Linux, esto puede conseguirse bien editando el lanzador gvSIG.sh añadiéndole este parámetro antes del símbolo "$@", o bien pasándole el parámetro a dicho lanzador.
- En Windows se puede modificar el fichero gvSIG.ini, en la línea que empieza por command, añadiendo el parámetro justo antes del símbolo #ARGS#. También es posible pasar el parámetro al comando gvSIG.exe en el momento de su ejecución.