Nsesa

Philip Luppens

Abstract

This is the official documentation for the Nsesa project.


Table of Contents

1. Introducing the Nsesa toolkit
What is Nsesa?
A little history
What's coming?
2. Getting started
Building the software
Installing & Setup
3. The architecture
Overview
The frontend
Overlaying
An overview of the front-end modules
The front-end modules in detail
Dialog
Amendment
Compare
InlineEditor
The backend
4. Advanced usage
Using the overlay factory
Sample code
5. Extending Nsesa
Changing the User Interface
Plugging in different implementations
An example: overriding the location string
6. Schema Support
Introduction
Getting started
Getting into the details

Chapter 1. Introducing the Nsesa toolkit

What is Nsesa?

The Nsesa toolkit provides an easy way to create applications that deal with structured content.

Nsesa?

The project name 'Nsesa' is coming from the the Akan word for 'change'.

A little history

With the advent of XML, structured content has become increasingly relevant for many types of content, ranging from configuration, exchange and data storage. The benefits are legio: structured content allows for easier querying and validation, but also enables transformation and linking.

In 2008, the European Parliament started the Authoring Tool for Amendments (AT4AM) project, to replace the Microsoft Word-based solution that was in use. Despite the available tools and plugins, the Microsoft Word application has been shown to be rather challenging to create fully structured content. With few constraints in place, the very flexibility of word processors became its own problem: users could accidentally introduce invalid content, which would, combined with the lexical misunderstandings and limited domain knowledge, result in hard-to-trace errors, and thus frustration for the user. Limited customization and inspection possibilities also meant that it would be impossible or impractical for plugins to assist the user in some tedious and error-prone tasks.

The European Parliament chose to develop a new browser-based application that would make the authoring of amendments much more simple by taking away the most error-prone and slow parts. By presenting the user with a document render like in word processors, but focusing on the core elements (proposing modifications to the document) and automating everything else, users found themselves to be able to save significant amounts of time while simultaneously increasing the quality of the content and reducing the amount of errors.

The core of the system was built around XML, which has proven to be a perfect fit for legislative documents. The frontend was written in Google Web Toolkit, with a three-tier architecture to support the persistence and workflow to further handle the legislative chain.

In 2012, the system had been responsible for more than a quarter million amendments, and its success paved the way for a more intense collaboration between both internal departments and external actors, with a heavy focus on using a more open and accessible document exchange. Several actors asked for the source of the software to be made available in order to benefit member states and parliaments worldwide and to provide more transparency and openness from the European Parliament.

In 2012 during a workshop of XML usage in the legislative chain, the Director-General of DG-ITEC, Vilella Giancarlo, officially stated his promise to make AT4AM available as open source. Even though it was deemed impossible to open the existing AT4AM project due to security reasons and because of its strong focus on the European Parliament, a new project was started as the first of its kind, to work on a new, elegant and extensible framework that would support open standards, and that could be used by everyone to author amendments, draft proposals and enrich the content.

What's coming?

At this moment, the team has focused on what we believe to be the most challenging and uncharted territory - the amending of proposals. Since this technically also requires basic drafting and marking up capabilities to be in place, it is definitely a massive undertaking.

At the same time, we do realize that there is the need for standalone modules for both drafting and marking up, translation, verification, voting, and so on. While the progress on the development of this modules is heavily impacted by available resources and collaboration, we do believe that the core of the toolkit is perfectly capable of handling all known use-cases and needs for these modules.

The initial focus has been on the frontend, since the backend is relatively well understood, and often already in place. Wiring up the backend is straightforward, and therefore considered to be less of a priority. With that said, we do want to provide a 'state of the art' backend that is capable of handling all requirements for organisations and parliaments worldwide.

Now of course the current frontend application for amending is not finished: features such as highlighting, notes, collaboration, more advanced handling of complex content such as images, tables and formulas are just a few of the things that are on the road map.

Chapter 2. Getting started

Building the software

In order to build the software, you should check out the following repositories. Note that you need to have Git and Maven installed.

                    
                    $ mkdir nsesa
                    $ cd nsesa

                    $ git clone https://bitbucket.org/pluppens/nsesa-diff.git
                    $ cd nsesa-diff
                    $ mvn clean install
                    $ cd ..

                    $ git clone https://bitbucket.org/pluppens/nsesa-server-api.git
                    $ cd nsesa-server-api
                    $ mvn clean install
                    $ cd ..

                    $ git clone https://bitbucket.org/pluppens/nsesa-server-impl.git
                    $ cd nsesa-server-impl
                    $ mvn clean install
                    $ cd ..

                    $ git clone https://bitbucket.org/pluppens/nsesa-editor.git
                    $ cd nsesa-editor
                    $ mvn clean install
                    $ cd ..

                    $ git clone https://bitbucket.org/pluppens/nsesa-editor-an.git
                    $ cd nsesa-editor-an
                    $ mvn clean install
                    $ cd ..
                    
                

After a successful compilation you will have 2 deployable war files:

  • nsesa-server-impl/target/services.war

  • nsesa-editor-an/target/editor.war

Both files need to be deployed in a Java application server such as Apache Tomcat or Jetty.

Installing & Setup

After you have successfully built both deployable war files (services.war and editor.war), you should install them in a Java application server such as Apache Tomcat or Jetty.

Installing these war files can be done by simply dropping them in the hot deploy folder (commonly webapps/). Upon successfully startup, you should have two new web applications that are accessible via the services/ and editor/ context path.

About the default configuration

By default, we assume the application services to be available on the localhost on port 8080. If this is not the case, you can override the default by placing a copy of the nsesa-editor-an.properties in your home directory, and overriding the URL to the services.

Using another database

The default database used by the services application is the H2 in-memory database, which is great for testing, but not so much when you want your data to be persisted across server restarts. To change it, copy the nsesa-server.properties file in your home directory, and uncomment and change the JDBC parameters (they are self-explanatory).

Chapter 3. The architecture

Overview

In order to get a better understanding of the architecture, we'll take a 5000ft overview first, and go into detail later on. In the first section we'll discuss the front end, which is arguably more exotic and therefore deserves the lion-share of explanation in this chapter.

The frontend

The frontend applications are built using the Nsesa toolkit, which allows developers to quickly create higher level abstractions from XML documents. So how does that work?

When working with XML documents, we are often dealing with low-level DOM elements, accessing attributes, adding child elements to parents, and so one. Hopefully, after we're done, we'll have a document that conforms to a given schema. While this certainly works, it's not quite ideal.

Imagine you writing code that deals with a certain business requirement, such as not allowing the user to create points under articles in an annex, while still ensuring that everything else is correct according to the schema (some business requirements are simply not expressable in a schema, because of missing context information - and even if it were possible, it would look *horrible*).

Let's go further - how about you wanting to validate a fragment of XML? Doing this is definitely not straightforward using traditional approaches.

The Nsesa toolkit aims to provide all that - and more. It has UI elements to display documents, and it uses a technique called 'overlaying', which we'll explain in the next section.

Overlaying

When Nsesa overlays a document, it encapsulates a DOM element - or a set of DOM elements - into a higher level element. For example, we might overlay the following:

                        
                        <div class="paragraph">
                            <span class="num">14.</span>
                            <span class="content"><p>This is a paragraph.</p></span>
                        </div>
                        
                    

And turn this from a DOM Element into a OverlayWidget class:

                        
                        Paragraph p = new OverlayWidgetImpl(element); // encapsulate the underlying element
                        p.getNumberingType(); // returns NumberingType.NUMBER
                        p.getFormat(); // returns Format.POINT
                        p.getLiteralIndex(); // returns '14.'
                        p.getUnformattedIndex(); // returns '14'
                        p.getInnerHTML(); // returns '<p>This is a paragraph.</p>'
                        
                    

As you can see, you can find quite a bit of information about the underlying element in a high level fashion. But we can go further - since we're in a tree, we can also query the parents and children, siblings, and more.

So how does the OverlayFactory know how to do the overlaying? Well, it uses an OverlayStrategy, a class that is responsible for providing access to properties (via DOM element attributes) and to the child elements (we ignore other types of nodes).

                        
                        OverlayWidget parent = p.getParentOverlayWidget();
                        List<OverlayWidget> children = parent.getChildOverlayWidgets();
                        p.next(mySelector); // returns the next overlay widget that matches mySelector's select() method
                        p.previousSibling(anotherSelector); // returns the previous sibling that matches anotherSelector
                        
                    

This is but a small selection of the available API to interact in a high level way with the overlaid document. But, while this is definitely helpful, we'd still be working pretty dynamically, and therefore, be at risk that we're violating the underlying XSD. So, can we go deeper?

Turns out - yes we can. By using the XSD to generate classes (much like JAXB) we can generate perfect overlay classes that only offer for example those attributes that are specified by the schema, and only allow those structures that are valid. How to generate these overlay classes can be found in another chapter. Let's continue now with an overview of the modules that are currently available.

An overview of the front-end modules

The core nsesa-editor project has the following GWT modules (in alphabetic order):

  • Amendment: this module contains everything related to amendments.

  • Compare: contains the comparison panel for amendment revisions.

  • Dialog: has the main dialog components to deal with different types of amendments.

  • Editor: basic functionality to deal with the editing of XML documents.

  • Inline: inline editor.

By itself, the core nsesa-editor project cannot do much - the actual implementation details are contained in the nsesa-editor-an project, which provides support for Akoma Ntoso 2 & 3. The nsesa-editor-an contains by itself 2 modules:

  • AkomaNtosoEditor: the actual amendment authoring tool with Akoma Ntoso 2 & 3 support, features multiple documents, 3 tabs per document, internationalization, related documents, etc.

  • DraftingEditor: a proof of concept. The aim is to build an advanced editor with drafting capabilities.

In the next section, we will go more in depth into each of the modules.

The front-end modules in detail

Core

The Core module contains the bulk of the classes related to the overlaying, events and UI, as well as the majority of the DTOs used in the GWT-RPC communication with the backend.

Without going too much into detail (there's always the Javadocs), there are some core classes and concepts that you are bound to encounter over and over again, so we'll explain them here.

About the use of Gin and custom scoping

As explained before, the Nsesa toolkit uses Gin to leverage its power of dependency injection. While this liberates us from a great deal of responsibility, it also complicates the design slightly because of its lack of easy custom scoping. While we did try to hide this complexity, you should still be aware of its existence, or risk falling into a trap when extending or adding functionality.

The most important thing to realize is that we use multiple Injectors - one for the application wide singletons, and one for each document controller. Since objects marked as singletons in Gin are effectively Injector-wide singletons, this little trick gives us document controller-wide singletons - or document scoped objects.

This does have some drawbacks though - injecting dependencies declared in the application injector into a document scoped class will result in circular dependencies and broken builds. Or you might end up with dependencies being duplicated - which is problematic if they are not stateless.

So, if you need certain dependencies, try to access them via the corresponding DocumentController (even if that means casting) - this will always ensure that you have the correct instance.

The first class is the ClientContext. This class holds the client context information (username, person details, roles, documentID, ...) and needs to be authenticated before usage.

A little word of caution

The ClientContext must be retrieved via the ClientFactory (see below), and in general is passed always to the backend to validate a request. Note however, that the client context itself is accessible in Javascript, so it can be tampered with, indicating that you must re-authenticate to ensure correctness!

Next up is the ClientFactory. This versatile class gives access to various dependencies that are related to the client context - including the actual ClientContext, the application- wide EventBus, the Scheduler, KeyboardListener, the core message bundle, runtime configuration, and PlaceController (see 'Places and Activities' in the official GWT docs). Just like the ServiceFactory, it should be retrieved via the DocumentController.

  • EventBus: the global eventBus, used for application-wide eventing.

  • Scheduler: a simple scheduler for running scheduled tasks.

  • KeyboardListener: if installed, allows you to register keyboard combinations, which will be exposed via the KeyComboEvent on the global event bus.

  • Configuration: this one is a bit special. What happens here is that the configuration returns a Javascript object that is loaded from the URL 'configuration.json' in the context root (usually '/editor'). This file, which is optional, can hold a JSON configuration object that you define where additional runtime parameters can be stored and retrieved. Since you are completely free to format this configuration object in your own way, we can only give you the full JavascriptObject to explore.

                            final ClientFactory clientFactory = documentController.getClientFactory();
                            String[] roles = clientFactory.getClientContext().getRoles();
                        

The third class we're going to look at is the ServiceFactory. This singleton gives access to the services that communicate with the backend using GWT-RPC. Without going too much into detail about the GWT-RPC mechanism and how to create Async counterparts (the official GWT documentation is flawless), we do want to list the current services:

  • GWTAmendmentService: gives access to the amendments (retrieve, save, delete, ...)

  • GWTDiffService: gives access to the diff of two objects or strings.

  • GWTDocumentService: retrieves the source text, related documents and translations.

  • GWTService: basic service to gain access to the ClientContext.

The ServiceFactory is injected into the DocumentController, and can be retrieved as such.

                            final ServiceFactory serviceFactory = documentController.getServiceFactory();
                            serviceFactory.getGwtDocumentService().getDocument(clientContext, documentID, callback);
                        

One of the most important classes is the OverlayFactory, which is responsible for translating the DOM element tree into a higher level tree of OverlayWidget (see the Overlaying section). It will use the OverlayStrategy to find out how to do the mapping.

Not just for entire documents

You can perfectly create overlay trees from any element - including amendment bodies, the content received from a Rich Text Editor (RTE).

You can retrieve it via the DocumentController.

The OverlayWidget and its default implementation OverlayWidgetImpl are the classes that provide easy access to various high level methods and properties. It's by far the most advanced class, but once you learn the methods, you'll understand the flexibility and power this class offers.

About the hierarchy

The OverlayWidgetImpl serves as the superclass for all generated Overlay classes (see Generation chapter), meaning that every generated class inherits the same methods, while overriding others. Since these classes are generated from an XSD, you should never have to extend it yourself (unless you want additional methods, and change the generation template).

Let's have a look at some of the methods that are available (starting with the simple ones):

  • overlayWidget.getNamespaceURI(): returns the full namespace for the underlying element.

  • overlayWidget.getType(): returns the type or local name of this element (eg. Paragraph, Article, ...). Combined with the namespace declaration, this should be enough to correctly identify an element.

  • overlayWidget.isAmendable(): checks if this overlay widget is amendable.

  • overlayWidget.getPreviousSibling(OverlayWidgetSelector selector) and its counterpart overlayWidget.getNextSibling(OverlayWidgetSelector selector): check the siblings in an extending way, returns the first one that matches the selector.

  • overlayWidget.previous(OverlayWidgetSelector selector) and its counterpart overlayWidget.next(OverlayWidgetSelector selector): unlike the sibling methods above, this one will not just evaluate siblings, but actually continue evaluating nodes from siblings and parents.

There are many more methods - giving access to the parent OverlayWiget, the child OverlayWidgets, any modifiers (think amendments), and so on. Because of all this context, getting access to an OverlayWidget in a callback or context element will give you all the available information necessary to take a (business) decision. Note that at all times you have access to the actual underlying DOM Element, and that those changes therefore will be reflected in the DOM.

The Core module also offers several UI components, most of which have been packaged hierarchically. Pretty much all components are built in the same way using a standard Model-View-Controller (MVC) approach.

What does MVC mean in Nsesa?

It means that for each {Component}, you'll get a {Component}Controller, {Component}View, {Component}ViewImpl, and so on. Every Controller can be backed by a model, and is used to calculate and set display information on the view, listen to the (Document)EventBus and callbacks from the View. The View, on the other had, merely displays and formats the information that is set by the controller.

The DocumentControllers is the main controller that represents a single physical document. It has methods to retrieve and display document objects and their content, and has its own DocumentInjector and DocumentEventBus for local communication.

About the event bus

Components used in the DocumentController (such as the DocumentHeaderController, the ActionBarController, the SourceFileController, ...) are all using the DocumentEventBus to communicate 'within' the document scope, where the DocumentController, which has access to the global EventBus and the DocumentEventBus, acts as a gateway, forwarding certain events if necessary from one bus to another.

Almost all components have a public 'registerListeners()' and counterpart 'removeListeners()' method that should be called by the parent component upon instantiation or destruction. As always: make sure to clean up everything, or you will risk severe memory leaks, especially with event buses.

Another important class is the RichTextEditor - and its implementation based on a wrapping of the CK editor. We have chosen the CK editor because of its maturity and extensibility, the plethora of available plugins and the fact that it gives a lot of possibilities to customize the editing of HTML. In order to work more easily with the CK editor, we have created a bridge so plugins can be created in Java rather than Javascript.

Dialog

The Dialog module is used for the main dialog that is shown when creating or editing an amendment. It works by analyzing both the action (a deletion amendment, a modification, a new element, ..) and the type of element that is used (a table, an image, some complex structure, ...). Based on these two parameters, it will propose a certain AmendmentUIHandler to handle the request.

Amendment

Amendments are simple components that implement the OverlayWidgetAware interfaces, making them stackable on an OverlayWidget. They contain the absolute minimal fields in order to be as flexible as possible (indeed, its body field contains a single transformed XML document). This makes it possible to get very creative when it comes to amendment modelling, but it means extra work when it comes to updating (a small part of) the content, such as the original parts, or the amendment parts.

Amendments are proposals for changes in a source text - be it an act, bill, or whatever. Before they are accepted, they have to be voted. Only once they are voted, they can be consolidated into a new text (which then might, subsequentially, be amended again).

Amendments usually consist of two parts: an original part, and an amendment part. The original part can be empty (in the case of a new structure), the amendment part can be empty (in case of a deletion), both can differ from one another (a modification), or other actions are possible (moving elements, replacing definitions, ..).

Of course, just having the original and amendment structures is not enough. Amendments are typically located somewhere in the proposal ('Article 3 - paragraph 2 - point A'), and they have usually one or more authors who might file on behalf of themselves, their committee, or their political group.

Finally, most amendments also feature a justification or notes to actors later in the legislative workflow. Depending on the environment, amendments might feature more extensive meta data or properties, but these are the most commonly found.

Now, like we explained before, in order to have as much flexibility as possible, the amendments in Nsesa do not have separate properties - rather, they are retrieved from the XML body using x-path expressions (well, almost), and overlaid using the same overlay factories we used for the source text.

Amendments can be added to an OverlayWidget and change change their views (amendments have multiple views - one for in the proposal, and one for usage in the amendments listing in the amendment tab of the document controller.

Compare

The Compare components are used only to compare different revisions of a document or amendment.

InlineEditor

The Inline Editor module is quite simple and features only a single component to use a Rich Text Editor (RTE) on a single OverlayWidget. To use it, simply fire a AttachInlineEditorEvent on the global EventBus with the correct parameters. To remove it, fire a DetachInlineEditorEvent.

There can be only one

Note that there can be only one single inline editor - firing a second attach event without a previous detach event is setting you up for trouble.

The backend

The backend consists of 2 parts: an API with DTOs and an actual implementation that uses webservices over SOAP and REST (REST being currently unused).

The backend implementation uses the following technologies:

  • Spring: provides dependency injection for the service layer

  • Spring-data + JPA: persistence and data retrieval

  • CXF: exposes services as webservices

  • Jersey: exposes services as Restful

  • Geda: provides domain-to-dto translation

By default, an in-memory database (h2) is used, which is sufficient for demonstration purposes, but for permanent storare or production usage in general, we recommend using PostgreSQL.

Database indexes

JPA has no known annotation to indicate that an index should be created on a certain database column (or columns). Therefore, we've included a set of SQL scripts that can be used to generate some missing indexes (on PostgreSQL). Adapting to different SQL dialects should not be hard.

Chapter 4. Advanced usage

Using the overlay factory

There might be many reasons why you'd like to use overlaying, but basically anytime you need to analyze the structure, capture events or provide some kind of feedback or validation, you'd have to use the overlay factory. In this chapter, we'll have a look at some of the possible approaches to create your overlay tree via the overlay factory.

Sample code

                
                Div contentHolder = DOM.createDiv();
                contentHolder.setInnerHTML(myContentAsString);

                OverlayFactory overlayFactory = new AkomaNtoso30OverlayFactory(new DefaultOverlayStrategy());
                OverlayWidget root = overlayFactory.getAmendableWidget(contentHolder.getFirstChildElement());
                
            

At this point, we have an OverlayWidget called root, which will represent a tree. In order to walk this tree and attach for example a listener for UI events such as clicks, do the following:

                
                root.walk(new OverlayWidgetVisitor() {
                                @Override
                                public boolean visit(OverlayWidget visited) {
                                    visited.setUIListener(myUIListener);
                                    return true; // if false, we'll not visit the children of 'visited'
                                }
                            });
                
            

Note: you're not limited at all to what you can do during this tree walk: if you want to change the overlayWidget's element, then you can do that as well. Tree walking is one of the most important features in the Nsesa toolkit, and you should use it as often as necessary.

Performance?

A very good point. We've spent substantial effort trying to make sure that the overlaying process is as performant as possible. Yet, you might want to defer making changes that trigger a CSS reflow or repaint, as those operations can be very expensive, especially on large documents.

In general, we also advise you not to continue walking after you have found the node(s) you were looking for, and keep in mind that returning 'false' when doing a treewalk will not prevent the visitor from visiting the siblings of the node already visited.

Another thing to note is that we built in a special construct that we call 'overlay splitting', where the overlay factory will split off and temporarily pause in order to make sure that the browser does not throw 'Slow Script'-warnings, something that might happen on older Internet Explorer browsers, especially if the document tree is big.

Chapter 5. Extending Nsesa

Changing the User Interface

All (well, almost) all of the components in Nsesa are specified using GWT's UIBinder approach. This means that a) making changes to the HTML part is rather straightforward, and b) that a recompilation is necessary after every change.

What about using external CSS?

While it is definitely possible to use an external stylesheet, you have to be careful: any CSS class name that is not fixed by setting it in UIBinder will change because of the obfuscation pass during the compilation. So it's often better to use an internal.

Most of the time the same approach is used in a component (let's assume our component is named 'AuthorPanel'):

  • AuthorPanelController: the main controller (see MVC).

  • AuthorPanelView: the view interface.

  • AuthorPanelViewImpl: the default implementation of the view using GWT's UIBinder.

  • AuthorPanelViewImpl: the default implementation of the view using GWT's UIBinder.

  • AuthorPanelViewCSS: a GWT CSS resource class (with a matching AuthorPanelViewImpl.css).

  • AuthorPanelViewImpl.ui.xml: the GWT UIBinder template.

If your changes are purely UI or CSS related, try adapting the ui.xml file.

How to find your component

The Akoma Ntoso implementation of the Nsesa toolkit will, when ran in Out of Process Hosted Mode (OoPHM) will display a tooltip when hoovering a component, which you can then use to find the component responsible for the UI under your mouse cursor.

Plugging in different implementations

Often you want to override just a certain piece of code, or provide a slightly different implementation to fit your use case. In this section, we'll describe the mechanism for doing this.

The Nsesa toolkit heavily uses Gin to provide an easy to use mechanism that requires a certain familiarity with the codebase and chosen approach - which is different from a standard Gin project.

Depending on the injection scope (application vs document), you need to carefully consider what components in what module, or you'll risk ending up with broken builds, duplicate singletons, and so on. In general, when dealing with components on a document level, you should consider retrieving dependencies via the document controller, rather than via (constructor) injection.

An example: overriding the location string

In this section we'll show you how to change a small component using the infrastructure that is already in place.

Chapter 6. Schema Support

Introduction

One of the design goals of the Nsesa project was to make sure that we'd not be limited to just one schema. Flexibility to add schemas and/or support newer versions was a primary objective. In this chapter we'll show you can add new schema support, and how you can use this to the fullest.

Besides the generation of an overlay model, it's also possible to generate (hierarchical) CSS files for various purposes, or, if you like to extend the generation templates, anything that you'd wish to generate. Let's get started.

Getting started

To understand what is going on, let's first pick a schema for a format that is 'fit for human consumption', that is: we want the end result of the transformation to be a document that can be read by humans.

The schema of choice for this demonstration is DocBook. DocBook is a well-known schema that is used by authors and publishers to write books (hence its name). It's quite an extensive format, so manual approaches would be severely time consuming.

The first thing to do is locate an XSD for DocBook. You can find it on its website. Download it and put it somewhere accessible. Then, fire up the generator:

                
                $ java org.nsesa.editor.app.xsd.FileClassOverlayGenerator {package} {target_directory} {xsd}
                
            

For example, the following command could be entered (be aware of the trailing dot in the package name):

                
                $ java org.nsesa.editor.app.xsd.FileClassOverlayGenerator org.nsesa.editor.gwt.an.common.client.ui.overlay.document.gen. src/main/java/org/nsesa/editor/gwt/an/common/client/ui/overlay/document/gen/ docbook/docbook.xsd
                
            

Classpath

In order to run the OverlayGenerator, you need to make sure the nsesa-editor module is in your classpath. Failure to do so will result in ClassNotFoundExceptions, or your XSD not being found. Use your IDE to launch the program if possible.

Getting into the details

So what does this generated code actually do? If you have a look at the overlaying chapter, then you will see that we extract the models and entities from the XSD (via XSOM), and use it to capture all the information such as the hierarchy, attributes and allowed structure. This gives us a rich and detailed model, which we can the use to generate a very precise overlay element, and, thanks to this generated code, a fully type-safe library where the hierarchy can be used to simplify programming business rules tremendously.

But this is not the only thing that we can generate - other things are a myriad of CSS files for various applications. In order to use the CSS generation, you can use the following:

                
                $ java org.nsesa.editor.app.xsd.CssOverlayGenerator {CSS_output_path} {xsd} {templateName} {include_empty_css?}
                
            

For example:

                
                $ java org.nsesa.editor.app.xsd.CssOverlayGenerator /Users/jdoe/Projects/nsesa-editor-an/web/css/akomaNtoso-all.css akomaNtoso/akomantoso20.xsd overlayCss.ftl false
                
            

One thing to note is the use of the template (specified via the templateName): several templates are available:

  • overlayCSS.ftl: exports basic CSS for all elements, based on the properties specified in overlayCSS.properties (note: this supports the XSD hierarchies, so if you specify a CSS style for a base element, than this will be included for all its children as well, provided that you do not override them specifically). This file should be in your classpath.

    An example

    Suppose we have the following lines in our overlayCSS.properties: container=display:block;margin-top:1.5em; mycontainer=margin-top:2.5em;background-color:green; If we assume for a moment that 'mycontainer' extends the declaration of 'container' in the XSD, then all subclasses of 'container' will have the display:block and margin-top:1.5em properties; 'mycontainer' on the other side will inherit the display property, redefine the margin-top property and finally add a new background color.

    Use this as a starting point

    The primary reason for developing this was to make it more easy to assign CSS properties to hierarchies rather than to individual elements. Some XSDs specify more than 500 elements - doing them by hand would be rather time consuming.

    On the other hand: use this only for generating the general properties for your CSS, and use another CSS file with specific CSS declarations for your application where you can add individual CSS properties.

  • overlayCSSVisual.ftl: creates a CSS file that will be used whenever structure mode is enabled. It will wrap all tags in (random) colored representations to make sure the user can visually identify the underlying structure.

  • overlayCSSVisualColors.ftl: similar to the overlayCssVisual file, but this is meant to be used in the CKEditor when structure mode is toggled on.

As soon as the overlay classes are generated, they can be used in the codebase.