pxDoc extensibility

Download pxDoc Reference Guide in PDF and pxDoc Leaflet

pxDoc extensibility

Common extensibility tasks on the UI project

In this section, we will present some of the extensibility capabilities of pxDoc, based on examples:

  • Adapt the Command class of the UI project to directly bind variable styles with a stylesheet
  • Adapt the generation launching wizard to your needs

Adapt the generation launching wizard

Introduction

You might want to provide many generation parameters for your document generator, and users must be able to feed these generation parameters in the generation wizard (e.g. Title, Company name and Version of the document in the following examples).

You will taylor the generation launching wizard to propose this option

Adapt the Wizard class

First, you will add a page to the wizard by adding some code to the Wizard class

The Wizard class can be found in the src folder of the UI project linked to your generator.

Adapt the following code to your generator name, to add a new page to the generation wizard:

							package mygenerator.ui;
							
							/**
							 * Wizard class
							 */
							public class MyGeneratorWizard extends AbstractMyGeneratorWizard {
							
								@Override
								public void addPages() {
									super.addPages();
									addPage(new OptionPage("Custom Options")); 
								}
							
							}
Create a new page in the Wizard

Now, you will create the new Wizard page containing text fields for the required properties in a class named OptionPage.

Create a java file in the src folder of the UI project, and make it inherit from WizardPage and implement IPxDocWizardPage.

You will need to provide an implementation for 3 methods of the interface IPxDocWizardPage:

  • void fillLauncher(PxDocLauncher pLauncher): this method will be called for every IPxdocWizardPage of the generation wizard, filling your parameter values in the PxDocLauncher object.
  • IDialogSettings buildDialogSettings(): builds a IDialogSettings, that can be provided as default options for the next generation.
  • void loadDialogSettings(IDialogSettings pSettings): loads the previously built and saved IDialogSettings and initialize page widgets accordingly.

The code below defines the input text fields and send the content as properties to the pxDoc Launcher.

							package mygenerator.ui;
							
							import org.eclipse.jface.dialogs.DialogSettings;
							import org.eclipse.jface.dialogs.IDialogSettings;
							import org.eclipse.jface.wizard.WizardPage;
							import org.eclipse.swt.SWT;
							import org.eclipse.swt.layout.GridData;
							import org.eclipse.swt.layout.GridLayout;
							import org.eclipse.swt.widgets.Composite;
							import org.eclipse.swt.widgets.Label;
							import org.eclipse.swt.widgets.Text;
							
							import fr.pragmaticmodeling.pxdoc.runtime.launcher.PxDocLauncher;
							import fr.pragmaticmodeling.pxdoc.runtime.ui.eclipse.IPxDocWizardPage;
							import fr.pragmaticmodeling.pxdoc.runtime.ui.eclipse.wizard.IPxDocWizard;
							import fr.pragmaticmodeling.pxdoc.runtime.util.PxDocLauncherHelper;
							
							/**
							 * Wizard class
							 */
							
							public class OptionPage extends WizardPage implements IPxDocWizardPage {
							
								private static final String SETTINGS_ID = "SampleOptionsPage_Settings";
							
								private static final String SOCIETY = "society";
							
								private static final String DOCUMENT_TITLE = "title";
							
								private static final String DOCUMENT_VERSION = "version";
							
								private Text societyName;
							
								private Text titleText;
							
								private Text versionText;
							
								/**
								 * Modify implementation of pxDoc Wizard
								 * -------------------------------------
								 */
								protected IPxDocWizard getPxDocWizard() {
									return (IPxDocWizard) getWizard();
								}
							
								@Override
								public void fillLauncher(PxDocLauncher pLauncher) {
									PxDocLauncherHelper.addProperty(pLauncher, SOCIETY, societyName.getText());
									PxDocLauncherHelper.addProperty(pLauncher, DOCUMENT_TITLE, titleText.getText());
									PxDocLauncherHelper.addProperty(pLauncher, DOCUMENT_VERSION, versionText.getText());
								}
								
								public OptionPage(String pageName) {
									super(pageName);
									setTitle("Sample Option Page");
									setDescription("Illustrates how to implement a custom wizard page with generation parameters.");
								}
								
								
								/**
								 * Design of the new Wizard page
								 * --------------------------
								 */
							
							
								@Override
								public void createControl(Composite parent) {
									Composite composite = new Composite(parent, SWT.NULL);
									GridLayout gridLayout = new GridLayout(2, false);
									composite.setLayout(gridLayout);
							
									// Document title
									Label titleLabel = new Label(composite, SWT.WRAP);
									titleLabel.setText("Document Title :");
									titleText = new Text(composite, SWT.BORDER);
									titleText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
									titleText.setMessage("A title for your document");
							
									// Document version
									Label versionLabel = new Label(composite, SWT.WRAP);
									versionLabel.setText("Document Version :");
									versionText = new Text(composite, SWT.BORDER);
									versionText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
									versionText.setMessage("1.0");
							
									// Society
									Label societyLabel = new Label(composite, SWT.WRAP);
									societyLabel.setText("Society :");
									societyName = new Text(composite, SWT.BORDER);
									societyName.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
									societyName.setMessage("Society Name");
							
									composite.pack();
									// set the composite as the control for this page
									setControl(composite);
								}
							
								@Override
								public String getSectionId() {
									return SETTINGS_ID;
								}
							
								@Override
								public IDialogSettings buildDialogSettings() {
									DialogSettings settings = new DialogSettings(SETTINGS_ID);
									settings.put(SOCIETY, societyName.getText());
									settings.put(DOCUMENT_TITLE, titleText.getText());
									settings.put(DOCUMENT_VERSION, versionText.getText());
									return settings;
								}
							
								@Override
								public void loadDialogSettings(IDialogSettings pSettings) {
									if (pSettings != null) {
							
										// society
										String name = pSettings.get(SOCIETY);
										if (name == null) {
											name = "";
										}
										societyName.setText(name);
										// title
										String title = pSettings.get(DOCUMENT_TITLE);
										if (title == null) {
											title = "";
										}
										titleText.setText(title);
										// version
										String version = pSettings.get(DOCUMENT_VERSION);
										if (version == null) {
											version = "";
										}
										versionText.setText(version);
									}
							
								}
							
							}
							
Use the properties in your Generator

In your generator, you can get the properties and use them as you want:

							package mygenerator
							
							/**
							 * MyGenerator empty pxDoc generator. 
							 */
							pxDocGenerator MyGenerator
							{
							
								root template main(Object model) {
							
									document {
							
										var specTitle = getProperty("title") as String
										var societyName = getProperty("society") as String
										var version = getProperty("version") as String
										
										specTitle
										§
										societyName
										§
										version
										
									}
							
								}
							
							}
Result

Your new Wizard page is created and the properties are sent to the document:

Bind variable styles before deploying a generator

Use case

The use case is the following: you have developed a Generator using variable styles, but you do not want your final user to have to choose a stylesheet nor to bind the styles before the generation. Before deploying your Generator (as a feature, plugin,...), you will just need to modify the code of the Command class as below.

The Command class can be found in the src folder of the UI project linked to your generator.

Example with a generator named MyGenerator using the variable styles MyStyle1 and MyStyle2: these styles will be bound to Heading1 and Heading2 of the deployed stylesheet MyStylesheet.

							
							package mygenerator.ui;
							
							import com.google.inject.Inject;
							
							import fr.pragmaticmodeling.pxdoc.runtime.IPxDocGenerator;
							import fr.pragmaticmodeling.pxdoc.runtime.IStylesheetsRegistry;
							import fr.pragmaticmodeling.pxdoc.runtime.launcher.PxDocLauncher;
							import fr.pragmaticmodeling.pxdoc.runtime.util.PxDocLauncherHelper;
							
							public class MyGeneratorCommand extends AbstractMyGeneratorCommand {
								
								@Inject
								IStylesheetsRegistry stylesheetRegistry;
							
								@Override
								protected void initLauncher(PxDocLauncher launcher) {
									super.initLauncher(launcher);
									IPxDocGenerator pxDocGenerator = getPxDocGenerator();
									pxDocGenerator.setStylesheet(stylesheetRegistry.getStylesheet("MyStylesheet"));
									PxDocLauncherHelper.addStyleBinding(pxDocGenerator, "MyStyle1", "Heading1");
									PxDocLauncherHelper.addStyleBinding(pxDocGenerator, "MyStyle2", "Heading2");
								}
							
							}

pxDoc - Common Libraries and Plugins

pxDoc Common Libraries and Plugins are a set of pxDoc Modules and/or Java classes or interfaces providing useful extensions capabilities, that can be reused in any pxDoc Generator. Among others, you can:

  • query and include diagrams from your models,
  • provide generic templates for EMF model,
  • manipulate images (convert to AWT, split large images on several pages...)
  • ...

The language renderers are part of the pxDoc common plugins but they are described in a dedicated section.

There is no obligation to use these common libraries. You can also develop your own libraries, or extend the provided ones to match your specific needs.

Importing sources into your workspace

You can of course import the common libraries in your workspace to get a view on the common Modules and libraries included in pxDoc

Common Services / org.pragmaticmodeling.pxdoc.common.lib

The Common Services library is provided for demonstration purpose, and as a support for the set of generator examples shipped with pxDoc. You are free to use or not this library, or find inspiration to define your own "common lib".
CommonServices.pxdoc Module
The CommonServices.pxdoc module provides generic templates and functions that can be used by every documentation generator:
  • manage basic presentation of an object (so far, bookmarks management and objects descriptions retrieval). This is useful if your generator consumes many different java objects, with different ways of storing documentation, occasionally in many different documentation formats (raw text, html, markdown...)
  • provide basic reusable templates for optional or required documentation
  • provide a way to improve your documents, with the suggestImprovements boolean parameter. When set to true, warnings are generated in the document to enforce writing a documentation for the concerned objects.
  • ... and more useful generic services to come in the next releases!

CommonServices setup

OPTIONAL - When using the CommonServices module in your generator, you can inject your custom instance of IDescriptionProvider (see example below).

The IDescriptionProvider Interface

Among all properties that an object can hold, its human readable descriptions play a particular role. They are usually generated early when documenting an object, while other business properties are treated differently in following document sections.

The IDescriptionProvider interface gives the ability to return a description for every object processed by a pxDoc generator, as well as the expected description format (e.g., html, raw text, markdown...)

It is also responsible to provide a valid bookmark id for every object processed during generation, and decide which objects should be generated with a bookmark.

The class DefaultDescriptionProvider is the default implementation of IDescriptionProvider. It returns null as a description for every object, but is able to generate valid bookmarks for every object.

If you plan to use the CommonServices module, you will need to :

  • provide your own IDescriptionProvider by subclassing DefaultDescriptionProvider and provide your own implementations of the getDescription*(*) methods.
  • inject your IDescriptionProvider instance to the CommonServices when starting the generation (in the root template):
							root template main(Object model) {
							
								// Commons lib configuration : set a description provider able to get information about object description
								descriptionProvider = new MyDescriptionProvider(this)
								
								document {
									// content...
								}
							
							}

EMF Services / org.pragmaticmodeling.pxdoc.emf.lib

The Emf Services library is provided for demonstration purpose, and as a support for the set of generator examples shipped with pxDoc. You are free to use or not this library, or find inspiration to define your own "emf lib".

The EmfServices module is a set of templates and functions that allow to generate documentation for any EMF EObject. It takes benefit of the reflective EMF API to transform EObjects into documentation.

It defines two ways of EObject-to-doc transformation:

  • DocKind.form, for a form-like presentation of EObject features (attributes, references, methods)
  • DocKind.table, for a table regrouping all EObject features

You can get your documentation for an EObject by invoking the genericDoc template:

											var int level = 1
											var boolean recursive = true
											apply genericDoc(anEObject, DocKind.^table, level, recursive)

With:

  • anEObject, the model object
  • docKind, between form or table
  • level gives the heading (title) level that should be applied to the model object
  • recursive, to generate the documentation recursively on the object's children

And because some EObject's attributes are too technical, or rarely/never used, the EmfServices module provide a way to filter some EStructuralFeature from the generation. This is the role of the field excludedFeatures, which you can access the following way:

					root template main(Object model) {
					
						// exclude some EClasses
						excludedEClasses += EcorePackage.Literals.EANNOTATION
						// exclude EStructualFeatures
						excludedFeatures += EcorePackage.Literals.ECLASSIFIER__DEFAULT_VALUE
						excludedFeatures += EcorePackage.Literals.EMODEL_ELEMENT__EANNOTATIONS
						
						document {
							// content...
						}
					
					}
					

The EmfServices.pxdoc files contains comments to explain each template. Do not hesitate to have a look at it.

You can also refer to the EMF Example to see how these templates can be used.

EmfServices setup

OPTIONAL - When using the EmfServices module in your generator, you can inject your custom instances of:

  • labelProvider = myLabelProvider
  • contentProvider = myContentProvider

OPTIONAL - You can also define filters to exclude some model elements or features from the generation, by adding EClass or EStructuralFeature to the two following sets:

  • excludedEClasses += MyEmfPackage.Literals.MY_CLASS
  • excludedFeatures += MyEmfPackage.Literals.MY_CLASS__FEATURE

Diagram Services / org.pragmaticmodeling.pxdoc.diagrams.lib

The Diagram Services library is provided for demonstration purpose, and as a support for the set of generator examples shipped with pxDoc. You are free to use or not this library, or find inspiration to define your own "diagrams lib".
pxDoc Diagrams metamodel

A very simple EMF metamodel has been defined to manipulate diagrams in pxDoc. It is composed of a root object Diagrams, which contains a list of Diagram.

pxDoc Diagrams runtime

The diagrams library provides a set of interfaces to query diagrams from an object.

The diagrams library provides a set of interfaces to query diagrams from an object:

  • IDiagramProvider: this is the entry point to query diagrams, by invoking its createQuery(modelObject) method, which returns an IDiagramQueryBuilder object.
  • IDiagramQueryBuilder: defines a set of simple queries (by name, by type, by non empty diagrams...), that can be stacked to the query. When the query is complete, the execute() method has to be called to run the query. For example, there are 2 unit queries in the following query: diagramProvider.createQuery(sourceObjectWithDiagrams).type("CDB").notEmpty().execute.
  • IDiagramQuery: a POJO for the diagram query
  • IDiagramQueryRunner: will run the IDiagramQuery

The Diagram library provides useful templates to include diagrams.

You can also find samples in the examples provided with the pxDoc enablers

DiagramServices.pxdoc Module
Diagram queries with queryDiagrams(Object)

Samples of queries:

									
									// diagrams owned by the 'model' object
									var List<Diagram> ownedDiagrams = queryDiagrams(model).execute;
											
									// all diagrams owned directly and indirectly by the 'model' object
									var allOwnedDiagrams = queryDiagrams(model).searchNested.execute;
											
									// diagrams owned by the 'model' object, with some content
									var ownedNonEmptyDiagrams = queryDiagrams(model).notEmpty.execute;
											
									// look for a diagram owned by 'model' and named 'A diagram name'
									var diagramsByName = queryDiagrams(model).name('A diagram name').execute;
											
									// look for a diagram owned by 'model' with name staring with 'ABC'
									var diagramsByNameStartingWith = queryDiagrams(model).nameStartsWith('ABC').execute;
											
									// diagrams by type
									var diagramsByType_CDB = queryDiagrams(model).type('CDB').execute;
									var diagramsByType_ES = queryDiagrams(model).type('ES').execute;
									var diagramsByType_CDI = queryDiagrams(model).type('CDI').execute;
											
									// diagrams with type 'CDB' and name starting with 'MyString'
									var cdbStartingWithMyString = queryDiagrams(model).type('CDB').nameStartsWith('MyString').execute;
Include diagrams with the includeDiagram(Diagram) template

You can use the includeDiagram template to include your diagrams:

									
									// To insert the diagram image by yourself:
									for (diagram : myDiagrams) {
												
								// use the pxdoc 'image' keyword with diagram image file path 
								// stored in the 'path' diagram attribute (was 'data' in pxDoc v1.1 and previous).
								image path:diagram.path  
								// add a title with a style
								#Figure keepWithNext align:center {
								diagram.name
								}
								// include diagram documentation if any
								if (!diagram.documentation.empty) {
									#BodyText {diagram.documentation}
								}
							}

And you can get the source diagram object to extract some information from it (get the semantic elements...):

							
							for (diagram : myDiagrams) {
								//Below, 'MyDiagramType' to be replace according to your environment: 'DDiagram' for Sirius, 'Diagram' for Papyrus...
								var myDiagramObject=diagram.diagramObject as MyDiagramType 
							
								// Do whatever you need with the object...
							}

DiagramServices setup

REQUIRED - When using the DiagramServices module in your generator, you must inject an IDiagramProvider suited to your target platform.

diagramProvider = myToolDiagramProvider

Images Services / org.pragmaticmodeling.pxdoc.images.lib

The Images Services library is provided for demonstration purpose, and as a support for the set of generator examples shipped with pxDoc. You are free to use or not this library, or find inspiration to define your own "images lib".
Set of function allowing to:
  • Split large images in to several ones (and calculate automatically if split is necessary)
  • Crop an image, to display only a part of it
  • Scale an image to keep its aspect ratio
  • Convert a Java Image object into an AWT object (pxDoc can manipulate image files or AWT objects)