Creación de una extensión simple

Una extensión es una colección de clases y de recursos que proporcionan funcionalidad adicional a OrbisCAD. Desde la perspectiva del usuario es tan fácil como dejar caer un jar en el directorio de extensiones.


Vamos a ver el proceso de desarrollo de una extensión que instala un plugin que cierra la aplicación. El primer paso es escribir el plugin. Un plugin es una clase que añade un menú y/o un botón a la barra de herramientas y responde cuando el usuario los selecciona. Aquí se puede ver el código de un plugin sencillo que deshace la última operación de edición realizada. Una vez creado el plugin hay que crear la extensión que lo carga.


Trabajando con plugins

Un plugin es un objeto que realiza una acción simple en respuesta a la selección de un menú o a la pulsación de un botón por parte del usuario. En el código de OrbisCAD hay múltiples ejemplos de plugins ya que todos los menúesy botones que se ven en la aplicación son plugins. Los plugins se caracterizan por implementar la interfaz org.estouro.ui.extensionSystem.Plugin, la cual tiene cinco métodos: initialize, execute, run, isVisible e isEnable. El método initialize es invocado al iniciarse el sistema y es el encargado de instalar los menúes y botones. El método execute y run son invocados en ese orden cuando el usuario selecciona el menu o presiona el botón del plugin. Los métodos isEnabled e isVisible son invocados de forma regular para gestionar la activación del plugin.


Todos estos métodos reciben una instancia de org.estouro.ui.extensionSystem.PluginContext la cual da acceso al entorno de edición (tema activo, herramienta seleccionada, ...), ventana de la aplicación, etc, ... Para más información consultar el javadoc de dicha clase.


Creación de herramientas

Desde el punto de vista del usuario, las herramientas en OrbisCAD son botones en la barra de herramientas seleccionadas para operar con los temas en edición. Vienen definidas por un autómata de estados finitos. En cada estado del autómata se ejecuta un código de dibujado y en los estados finales se realiza la acción de edición concreta de la herramienta.


En la wikipedia se puede encontrar una definición bastante útil de lo que es un autómata. El autómata de la herramienta define en cualquier estado qué operaciones pueden realizarse y cuáles no. El siguiente ejemplo es bastante explicativo. En él se construye una herramienta que inserta linestrings en el tema en edición.





Lo primero que hay que hacer es definir el comportamiento de la herramienta con un autómata especificado mediante XML. Dicho XML será procesado por el generador de herramientas de OrbisCAD y de él se obtendrá una clase que implementa el autómata. La herramienta será una segunda clase que heredará de la clase generada y deberá implementar el código a ejecutar en cada estado del autómata. Veamos un ejemplo.


<automaton initial-status="Standby"

package="org.estouro.tools.generated"

name="Line"

icon="line.png"

tooltip="line_tooltip"

command="line">

<node name="Standby" text="line_standby">

<transition to="Point" code="point"/>

</node>

<node name="Point" text="line_point">

<transition to="Point" code="point"/>

<transition to="Done" code="t" label="line_point_to_done"

on-exit="true"/>

</node>

<node name="Done">

<transition to="Standby" code="init"/>

</node>

<node name="Cancel"/>

<transition to="Cancel" code="esc" label="cancel"/>

</automaton>


Para obtener la clase que implementa el automata se dispone de un generador en la clase org.estouro.tools.AG que toma como parámetros un directorio donde se encuentran los XML de los autómatas, el directorio base de los fuentes y el paquete dentro de dicho directorio:

java org.estouro.tools.AG ./automata/ ./src/ org.estouro.tools.generated


El nombre de los ficheros con los automatas deben terminar en “fsa.xml”. Una vez generada la clase hay que crear otra clase que herede de aquella. Dicha clase deberá implementar para cada estado del autómata un par de métodos con el código a ejecutar al llegar a dicho estado y con el código de dibujado en dicho estado. Además de estos métodos se deberán implementar los métodos isEnabled e isVisible que gestionan la visibilidad y activación de la herramienta. A continuacion se presenta la herramienta para crear lineas aunque se dispone de muchos más ejemplos en el código fuente de OrbisCAD en el paquete org.estouro.tools.


public class LineTool extends Line {


private ArrayList<Coordinate> points = new ArrayList<Coordinate>();


public void transitionTo_Standby(PluginContext ctx)

throws FinishedAutomatonException, TransitionException {

points.clear();

}


public void transitionTo_Point(PluginContext ctx)

throws FinishedAutomatonException, TransitionException {

EditionContext ec = ctx.getEditionContext();

points.add(new Coordinate(ec.getValues()[0], ec.getValues()[1]));

}


public void transitionTo_Done(PluginContext ctx)

throws FinishedAutomatonException, TransitionException {

if (points.size() < 2)

throw new TransitionException(“La linea ha de tener al menos dos puntos”);

try {

EditionContext ec = ctx.getEditionContext();

Theme t = ec.getActiveTheme();

DriverMetadata metadata = t.getDriverMetadata();

Value[] row = new Value[metadata.getFieldCount()];

for (int i = 0; i < row.length; i++) {

row[i] = ValueFactory.createNullValue();

}

LineString ls = new GeometryFactory().createLineString(points

.toArray(new Coordinate[0]));

com.vividsolutions.jts.geom.Geometry g = ls;

if (ctx.getEditionContext().getActiveTheme().getGeometryType() == SpatialDataSource.MULTILINE) {

g = new GeometryFactory()

.createMultiLineString(new LineString[] { ls });

}

if (!g.isValid()) {

throw new TransitionException(“No es una línea válida”);

}

row[t.getSpatialFieldIndex()] = FMapUtilities.getGeometry(g);

t.insertFilledRow(row);

} catch (DriverException e) {

throw new TransitionException(e);

}


transition(ctx, "init");

}


public void transitionTo_Cancel(PluginContext ctx)

throws FinishedAutomatonException, TransitionException {


}


public void drawIn_Standby(PluginContext ctx, Graphics g)

throws DrawingException {


}


public void drawIn_Point(PluginContext ctx, Graphics g)

throws DrawingException {

EditionContext ec = ctx.getEditionContext();

Point2D current = ec.getLastRealMousePosition();


ArrayList<Coordinate> tempPoints = (ArrayList<Coordinate>) points

.clone();

tempPoints.add(new Coordinate(current.getX(), current.getY()));

LineString ls = new GeometryFactory().createLineString(tempPoints

.toArray(new Coordinate[0]));

Geometry geom = FMapUtilities.getGeometry(ls);

try {

SpatialDataSource sds = ec.getDrawingDataSource();

sds.beginTrans();

sds.insertFilledRow(new Value[] { geom });

sds.commitTrans();

} catch (DriverException e) {

} catch (FreeingResourcesException e) {

}


if (!ls.isValid()) {

throw new DrawingException(“No es una línea válida”);

}

}


public void drawIn_Done(PluginContext ctx, Graphics g)

throws DrawingException {


}


public void drawIn_Cancel(PluginContext ctx, Graphics g)

throws DrawingException {


}


public boolean isEnabled(PluginContext ctx) {

EnableUtils eu = ctx.getEnableUtils();

return (eu.isGeometryTypeEqualTo(SpatialDataSource.LINE) || eu

.isGeometryTypeEqualTo(SpatialDataSource.MULTILINE))

&& eu.isThemeWritable();

}


public boolean isVisible(PluginContext ctx) {

return true;

}


}


Extensión mediante scripts. Scripting

Para extender OrbisCAD mediante scripts se dispone del ScriptManager, en el cual se pueden definir scripts junto con su ubicación en el menú principal



Driver extension

File driver

DB driver