The Forms Model API provides a set of Java classes for manipulating form definitions in Instant JChem schemas.

Since: 6.1

Concepts

One of the key aspects in IJC forms design is the separation of a form definition from its UI implementation. This allows forms to be defined independently on the way how they are displayed and to have multiple different UI implementations for visualizing the same form. One such implementation is a Swing-based forms UI in IJC desktop client. Another implementation is the Wicket-based web UI in IJC server. All these implementations can share and use the same form definitions.

The form definition is essentially an XML comprised from fragments representing all parts of a form. The structure of this XML plays a role of an API contract between all UI implementations. While using an XML as the API contract is possible it has many practical limitations. One of the major drawbacks is that it is generally hard to manipulate form definitions directly in XML from a Java code or Groovy scripts. Another drawback is that the XML format is hard to maintain and evolve and that the XML itself cannot be used as a live in-memory representation of a form.

The Forms Model is designed to address most of the deficiencies of the XML form definitions. It is a set of Java classes that can represent a complete form definition. Instances of these classes can either be loaded from the existing XML format or they can be created from scratch. All modifications made on the Forms Model objects can be persisted back to the XML format.

The Forms Model is not aimed to replace the XML format as such. It is meant to become the formal API contract for all parties interested in using or manipulating form definitions. This includes the form UI implementations as well as any IJC or third party code in plugin or script that accesses forms. The XML format is now merely playing a role of a persistence layer and its direct use is discouraged. The future versions of IJC products might change the XML format or even stop using it altogether in favor of some other persistence format.

The API conventions

The Forms Model is written as a simple Java class library and packaged in a single jar file. While this makes it simple for use it leads to several difficulties in API design. Any API is in fact a contract between the API maintainers and the API clients. The key part of this contract is defining the actual subject of the contract - what classes and methods belong to the API and what classes and methods don't. As simple as this may sound it is one of the biggest sources of confusion and problems in the API design.

The Forms Model Java class library contains classes that are meant to be used by the API clients as well as classes which comprise the internal implementation behind the API and are meant to stay private. Unfortunately, the Java does not possess module system supporting such distinction yet and do not allow API maintainers clearly declare what packages and classes are part of their API. Hence many API maintainers rely on conventions and the Forms Model does the same.

  • The Forms Model API is defined at the packages level using a package naming convention to distinguish public (exported) packages from private (implementation) packages. Any package with the api or spi component in its name is a public (exported) API package. All other packages are private and do not belong to the API.
  • The classes, methods and fields API status in the public packages is derived from their Java language visibility. For example all public classes and their public or protected methods and fields in a public package belong to the API.
  • All parts of the Forms Model API are documented in Javadoc, which is published.

While these conventions are useful for declaring the API they will not prevent anybody from using non-API classes. Adhering to these conventions is the responsibility of each API client. Failing to do so in the clients code is likely to result in this code to no longer function or even compile with future versions of the Forms Model class library. The API maintainers are not obliged to preserve backwards compatibility of non-API classes.

Use Cases

Create new form

        DFDataTree dataTree = ...;
        Form form = new Form();
        DFViews.create(dataTree, form, "Sample form");
    

Add widgets to a form

        Form form = new Form();

        // label
        LabelWidget labelWidget = new LabelWidget("New label");
        labelWidget.setSize(100, 50);
        labelWidget.setFont(new Font("Arial", Font.Style.BOLD, 16));
        form.addWidget(labelWidget);
    

Bind widgets to a field

        DFEntity entity = ...;
        DFField field = ...;

        // create textbox widget
        TextBoxWidget textBoxWidget = new TextBoxWidget();
        textBoxWidget.setPosition(0, 550);
        textBoxWidget.setSize(400, 50);

        // bind field
        textBoxWidget.setField(new FieldReference(entity.getId(), field.getId()));

        // set renderer
        textBoxWidget.setRenderer(new TextRenderer());

        Form form = new Form();
        form.addWidget(textBoxWidget);
    

Create new grid

        DFDataTree dataTree = ...;
        DFEntity entity = ...;
        DFField fCdId = ...;
        DFField fStructure = ...;
        DFField fMolWeight = ...;
        DFField fFormula = ...;
        DFField fIupacName = ...;

        TableWidget tableWidget = new TableWidget();
        tableWidget.setRowHeight(100)
        tableWidget.setHeaderRows(2)
        Border border = new Border()
        border.setBorderType(Border.BorderType.EMPTY)
        tableWidget.setBorder(border)

        // add a column for 'CdId' field
        FieldReference fRefCdId = new FieldReference(entity.getId(), fCdId.getId());
        IntegerRenderer integerRenderer = new IntegerRenderer();
        tableWidget.addColumn(fRefCdId, integerRenderer);

        // add a column for 'Structure' field
        FieldReference fRefStructure = new FieldReference(entity.getId(), fStructure.getId());
        StructureRenderer structureRenderer = new StructureRenderer();
        tableWidget.addColumn(fRefStructure, structureRenderer);

        // add a column for 'Mol Weight' field
        FieldReference fRefMolWeight = new FieldReference(entity.getId(), fMolWeight.getId());
        FloatRenderer floatRenderer = new FloatRenderer();
        tableWidget.addColumn(fRefMolWeight, floatRenderer);

        // add a column for 'Formula' field
        FieldReference fRefFormula = new FieldReference(entity.getId(), fFormula.getId());
        TextRenderer textRenderer = new TextRenderer();
        tableWidget.addColumn(fRefFormula, textRenderer);

        // add a column for 'IUPAC name' field
        FieldReference fRefIupacName = new FieldReference(entity.getId(), fIupacName.getId());
        MultiLineTextRenderer multiLineTextRenderer = new MultiLineTextRenderer();
        tableWidget.addColumn(fRefIupacName, multiLineTextRenderer);

        Grid grid = new Grid(tableWidget);
        DFViews.create(dataTree, grid, "Sample grid");
    

Load existing form or grid

        DFView formView = ...;
        Form form = DFViews.loadForm(formView);

        DFView gridView = ...;
        Grid grid = DFViews.loadGrid(gridView);
    

Find existing widget by it ID

Note that ID for a widget, used in the code snippet below, might be obtained by selecting a widget on IJC form and invoking Tools -> Experimental -> Display Widget ID from IJC menu.
        Form form = ...;
        LabelWidget labelWidget = form.getWidgetById("92abcb7b-b18a-46aa-a0b1-8ba12288b7cd");
    

Technical limitations

The current implementation of the infrastructure behind the Forms Model API has several limitations.

  • The Forms Model objects are not live objects. They can be loaded from the XML format and will reflect an existing IJC form, but they will not be connected in any way with a live UI implementation objects used for displaying that particular form. This means that manipulating the Forms Model objects will not have any directly visible effect on the displayed form. The changes made to the Forms Model objects have to be persisted in the XML format first and then the displayed form has to be reloaded in order for the changes to become visible.
    We understand that this is a significant deficiency and will improve the infrastructure further to remove the deficiency.
  • The Forms Model objects are not thread-safe and no one should attempt to manipulate the same object from multiple threads. Any concurrent access to the same Forms Model objects may result in breaking the model.
  • All Forms Model classes support Java serialization. The Java serialization, however, is not meant to be used as a long term persistence format. It is supported mainly for the benefits of web-based API clients, which in certain situations may find this useful. The Java serialization is not used internally by the Forms Model infrastructure and the Forms Model classes may not maintain the same serialization format in future version.
  • It is currently not possible to extend the Forms Model by implementing your own widgets, renderers or any other part of the API.
  • Not all widgets are supported. Outline widget and charts cannot be created or manipulated using Forms Model API. Nevertheless, forms loaded from the existing XML format will not loose these widgets when it is stored using Forms Model.
Packages 
Package Description
com.chemaxon.ijc.form.api  
com.chemaxon.ijc.form.api.renderers  
com.chemaxon.ijc.form.api.util