Generate Word Doc using Open API for ArchiMate Diagram

Hi All,

I have the following requirement, if someone can help point me to useful code/ docs that can help
a) My project has a ArchiMate diagram
b) My diagram has ‘N’ different states ( captured as layers ) - so for example state1, state2 and state3 … so on
c) I have written some template XMLs that are Diagram based for iterating and rendering tabular data.

The requirement is to use open API and generate a word document ( choosing my own base doc word template and styles ) and generate sections for state1, then sections for state 2 and then sections for state 3 + state 4 and so on. The states ( layers ) could be one or many and some sections combine multiple states as well - so figuring out the diagram and the layers need to be dynamic ( based on name convention ). Hence thinking of Open API. I would be happy with a solution that allows me to choose the diagram/ layers programmatically and then apply the XML templates to it via the Open API code in Java - so I have flexibility to search for diagrams and layers they contain and dynamically decide the layers to include in sections of the document.

Can anyone point me to some reference material/ code that can help me know how to do this - generate a word document using Open Api.

Any help is highly appreciated please, thanks.

Regards

Lalit Nagpal

You my specify the ‘layerFilters’ into your document’s content.
As the following sample code, it will find out an ArchiMate diagram, and a Document (IReportDiagramUIModel) first.
Get the content (IRDOOTemplate) from the document, and find out the layerFilter by your ArchiMate diagram’s id. (asume the 1st content is generated from your Template XML)
Specify your ‘state’ (layer name) into the layerFilter.
Result as screenshot:

package com.vp.plugin.sample.doclayers.actions;

import java.awt.*;
import java.awt.event.*;
import java.io.*;

import javax.swing.*;

import com.vp.plugin.*;
import com.vp.plugin.action.*;
import com.vp.plugin.diagram.*;
import com.vp.plugin.model.*;
import com.vp.plugin.view.*;

public class DocLayersActionController implements VPActionController {
	
	public DocLayersActionController() {
	}
	
	@Override
	public void performAction(VPAction aAction) {
		MyDialogHandler lDialogHandler = new MyDialogHandler();
		ApplicationManager.instance().getViewManager().showDialog(lDialogHandler);
		
		String lState = lDialogHandler.getState();
		if (lState != null) {
			
			File lOutput = new File("a.docx");
			
			IProject lProject = ApplicationManager.instance().getProjectManager().getProject();
			
			IArchiMateDiagramUIModel lArchiMateDiagram = null;
			IReportDiagramUIModel lDoc = null;
			for (IDiagramUIModel lDiagram : lProject.toDiagramArray()) {
				if (lDiagram instanceof IArchiMateDiagramUIModel && "Transition Architectures".equals(lDiagram.getName())) {
					lArchiMateDiagram = (IArchiMateDiagramUIModel) lDiagram;
				}
				else if (lDiagram instanceof IReportDiagramUIModel && "DocA".equals(lDiagram.getName())) {
					lDoc = (IReportDiagramUIModel) lDiagram;
				}
			}
			
			if (lArchiMateDiagram != null && lDoc != null) {

				IModelElement lModelElement = lProject.getModelElementByAddress(lDoc.getGenericXMLElementAddress());
				if (lModelElement instanceof IReportDiagramDetails) {
					IReportDiagramDetails lDocDetails = (IReportDiagramDetails) lModelElement;
					
					IModelElement lTemplate = lDocDetails.getTemplateByIndex(0);
					if (lTemplate instanceof IRDOOTemplate) {
						IRDOOTemplate lT = (IRDOOTemplate) lTemplate;
						
						for (ICompositeValueSpecification lLayerFilter : lT.toLayerFilterArray()) {
							if (lArchiMateDiagram.getId().equals(lLayerFilter.getName())) {
								/*
									Possible Values:
									- 	@all - All content of this diagram will be included in the document. (Even though some layers may be invisible in diagram)
									- 	@followDiagram - Follow the visibility of the layers set to the actual diagram. Layers that are set visible will be included here, likewise hidden layers will be excluded. Simply put, what you can see in the document will be exactly the same as the real diagram.
									->	name - Include the specified layer only.
									- 	@exclude:name - Exclude the specified layer.
									- 	name1, name2, name3, ... - Include the specified layers only. ", " is used as a delimiter.
									- 	@exclude:name1, name2, name3, ... - Exclude the specified layers. ", " is used as a delimiter.
									- 	${...} - Use a variable to specify the name/names of the layers to be included. User has to specify the value of the variable in Doc. Composer.
									- 	@exclude:${...} - Use a variable to specify the name/names of the layers to be excluded. User has to specify the value of the variable in Doc. Composer.
								 */
								lLayerFilter.setValue(lState); // include this 'state'
								
								ApplicationManager.instance().getDocumentationManager().generateDocComposerWord(lDoc, lOutput, null);
								ApplicationManager.instance().getViewManager().showMessageDialog(null, "Generated");
							}
						}
					}
				}
			}
			
		}
	}
	
	@Override
	public void update(VPAction aAction) {
	}
	
	
	private static class MyDialogHandler implements IDialogHandler {
		
		private final JPanel _component;
		private IDialog _dialog;
		
		private String _state;
		
		public MyDialogHandler() {
			_component = new JPanel(new BorderLayout());
			
			JPanel lBottomPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
			JButton lOkButton = new JButton("OK");
			JButton lCloseButton = new JButton("Close");
			lBottomPanel.add(lOkButton);
			lBottomPanel.add(lCloseButton);
			
			JLabel lLayerLabel = new JLabel("State:");
			final JTextField lLayerField = new JTextField();
			JPanel lPanel = new JPanel();
			{
				GridBagLayout lLayout = new GridBagLayout();
				lPanel.setLayout(lLayout);
				
				GridBagConstraints lCons = new GridBagConstraints();
				lLayout.setConstraints(lLayerLabel, lCons);
				lCons.insets.left = 2;
				lCons.fill = GridBagConstraints.HORIZONTAL;
				lCons.weightx = 1;
				lLayout.setConstraints(lLayerField, lCons);
			}
			lPanel.add(lLayerLabel);
			lPanel.add(lLayerField);
			
			_component.add(lPanel, BorderLayout.CENTER);
			_component.add(lBottomPanel, BorderLayout.SOUTH);

			lOkButton.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent aE) {
					_state = lLayerField.getText();
					_dialog.close();
				}
			});			
			lCloseButton.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent aE) {
					_dialog.close();
				}
			});
		}
		
		@Override
		public void prepare(IDialog aDialog) {
			aDialog.setTitle("Select State");
			aDialog.setSize(400, 200);
			
			_dialog = aDialog;
		}
		@Override
		public Component getComponent() {
			return _component;
		}
		@Override
		public void shown() {
		}
		@Override
		public boolean canClosed() {
			return true;
		}
		
		public String getState() {
			return this._state;
		}
	}
	
}

PS:
There is a bug on the above sample code:

ApplicationManager.instance().getDocumentationManager().generateDocComposerWord(lDoc, lOutput, null);

The last parameter (null) should be a JComponent, but now, if passing a null, an exception may be thrown.
It is just fixed but the fix is not yet ready in latest patch.
You may wait for the patch (may be available in several hours, I will post here when it is available),
or, if your plugin will show a Dialog with a JComponent (e.g. ContentPane), you may pass your JComponent to the generateDocComposerWord(...).

The latest patch build (20200430af or later) is ready.
Please update the software to latest patch build to get the problem fixed.
Details about update to latest patch can be found at

1 Like

Thanks a lot Peter.

Any idea why the filter doesn’t work in my case.

	reportDiagramUIModel = (IReportDiagramUIModel) diagramManager.createDiagram(IDiagramTypeConstants.DIAGRAM_TYPE_REPORT_DIAGRAM);
	reportDiagramDetails = reportDiagramDetailsContainer.createReportDiagramDetails();
	reportDiagramUIModel.setGenericXMLElementAddress(reportDiagramDetails.getAddress());

	Iterator<IReportDiagramDetailsContainer> reportDiagramIterator = project.modelElementIterator(IModelElementFactory.MODEL_TYPE_REPORT_DIAGRAM_DETAILS_CONTAINER);
	if (reportDiagramIterator.hasNext()) {
		reportDiagramDetailsContainer = reportDiagramIterator.next();
	} else {
		reportDiagramDetailsContainer = IModelElementFactory.instance().createReportDiagramDetailsContainer();
	}

	reportDiagramDetails = reportDiagramDetailsContainer.createReportDiagramDetails();

	IRDOOTemplate currentIRDOOTemplate = reportDiagramDetails.createRDOOTemplate();

	currentIRDOOTemplate.setTemplateURI(templateName);
	currentIRDOOTemplate.setSourceType(1);
	currentIRDOOTemplate.setSourceId(archiMateDiagram.getId());

	List<String> currentLayerList = layerNames.get(layersToFilterOn);
	
	// I tried this specifying a single variable as well with all comma separated names and
	// also as below - iterating and specifying separate ICompositeValueSpecification
	for (String namesToFilterOn: currentLayerList) {
		ICompositeValueSpecification currentAppLayerValueSpec = IModelElementFactory.instance().createCompositeValueSpecification();
		currentAppLayerValueSpec.setValue(namesToFilterOn);
		currentIRDOOTemplate.addLayerFilter(currentAppLayerValueSpec);
	}
	
	reportDiagramDetails.addTemplate(currentIRDOOTemplate);

	diagramManager.openDiagram(reportDiagramUIModel);

	// I have filtered but it shows all layers

Please set your ArchiMate Diagram’s ID into ICompositeValueSpecification.setName(...)
in IReportDiagramDetails.layerFilters,
1 ICompositeValueSpecification for 1 diagram,
.name = diagram.id
.value = included/excluded layer(s)

and, about the ‘value’ of ICompositeValueSpecification:

/*
	Possible Values:
	- 	@all - All content of this diagram will be included in the document. (Even though some layers may be invisible in diagram)
	- 	@followDiagram - Follow the visibility of the layers set to the actual diagram. Layers that are set visible will be included here, likewise hidden layers will be excluded. Simply put, what you can see in the document will be exactly the same as the real diagram.
	->	name - Include the specified layer only.
	- 	@exclude:name - Exclude the specified layer.
	- 	name1, name2, name3, ... - Include the specified layers only. ", " is used as a delimiter.
	- 	@exclude:name1, name2, name3, ... - Exclude the specified layers. ", " is used as a delimiter.
	- 	${...} - Use a variable to specify the name/names of the layers to be included. User has to specify the value of the variable in Doc. Composer.
	- 	@exclude:${...} - Use a variable to specify the name/names of the layers to be excluded. User has to specify the value of the variable in Doc. Composer.
*/

e.g.
if you want to

  • include one layer , call setValue(“name1”)
  • include several layers, call setValue(“name1, name2, name3”)
  • exclude one layer, call setValue("@exclude:name1");
  • exclude one layer, call setValue("@exclude:name1, name2, name3");

And there is a question in your code:

1 Like

Thanks a lot Peter.