summaryrefslogtreecommitdiffstats
path: root/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/helper/JAXBHelper.java
blob: 84a3e28e40d1a772f0f427d2f75e61026dac4f12 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
/*******************************************************************************
 * Copyright (c) 2013 Pascal Hirmer.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and the Apache License 2.0 which both accompany this distribution,
 * and are available at http://www.eclipse.org/legal/epl-v10.html
 * and http://www.apache.org/licenses/LICENSE-2.0
 *
 * Contributors:
 *    Pascal Hirmer - initial API and implementation
 *******************************************************************************/

package org.eclipse.winery.topologymodeler.addons.topologycompleter.helper;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import org.eclipse.winery.common.ModelUtilities;
import org.eclipse.winery.common.Util;
import org.eclipse.winery.model.tosca.Definitions;
import org.eclipse.winery.model.tosca.TDefinitions;
import org.eclipse.winery.model.tosca.TEntityTemplate;
import org.eclipse.winery.model.tosca.TNodeTemplate;
import org.eclipse.winery.model.tosca.TRelationshipTemplate;
import org.eclipse.winery.model.tosca.TRelationshipTemplate.SourceElement;
import org.eclipse.winery.model.tosca.TServiceTemplate;
import org.eclipse.winery.model.tosca.TTopologyTemplate;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;

/**
 * This class contains methods for marshalling and unmarshalling a topology XML string via JAXB.
 *
 */
public class JAXBHelper {

	private static final org.slf4j.Logger logger = LoggerFactory.getLogger(JAXBHelper.class.getName());

	/**
	 * This constant is used in the buildXML method which add coordinates to Node Templates so they
	 * are arranged properly in the Winery topology modeler.
	 *
	 * The x coordinate is constant because it is assumed that a stack of NodeTemplates is displayed.
	 */
	private static final String NODETEMPLATE_X_COORDINATE = "500";

	/**
	 * This method creates an JAXB Unmarshaller used by the methods contained in this class.
	 *
	 * @return the JAXB unmarshaller object
	 *
	 * @throws JAXBException
	 *             this exception can occur when the JAXBContext is created
	 */
	private static Unmarshaller createUnmarshaller() throws JAXBException {
		// initiate JaxB context
		JAXBContext context;
		context = JAXBContext.newInstance(Definitions.class);

		return context.createUnmarshaller();
	}

	/**
	 * This method returns a {@link TTopologyTemplate} given as XML string as JaxBObject.
	 *
	 * @param xmlString
	 *            the {@link TTopologyTemplate} to be unmarshalled
	 *
	 * @return the unmarshalled {@link TTopologyTemplate}
	 */
	public static TTopologyTemplate getTopologyAsJaxBObject(String xmlString) {
		try {

			logger.info("Getting Definitions Document...");

			StringReader reader = new StringReader(xmlString);

			// unmarshall the XML string
			Definitions jaxBDefinitions = (Definitions) createUnmarshaller().unmarshal(reader);
			TServiceTemplate serviceTemplate = (TServiceTemplate) jaxBDefinitions.getServiceTemplateOrNodeTypeOrNodeTypeImplementation().get(0);

			logger.info("Unmarshalling successful! ");

			return serviceTemplate.getTopologyTemplate();

		} catch (JAXBException e) {
			logger.error(e.getLocalizedMessage());
		}
		return null;
	}

	/**
	 * This method returns {@link TRelationshipTemplate}s as a JaxBObject.
	 *
	 * @param xmlString
	 *            the {@link TRelationshipTemplate} to be unmarshalled
	 *
	 * @return the unmarshalled {@link TRelationshipTemplate}
	 */
	public static List<TRelationshipTemplate> getRelationshipTemplatesAsJaxBObject(String xmlString) {
		try {
			StringReader reader = new StringReader(xmlString);

			// unmarshall
			Definitions jaxBDefinitions = (Definitions) createUnmarshaller().unmarshal(reader);
			TServiceTemplate serviceTemplate = (TServiceTemplate) jaxBDefinitions.getServiceTemplateOrNodeTypeOrNodeTypeImplementation().get(0);

			List<TRelationshipTemplate> foundRTs = new ArrayList<>();
			for (TEntityTemplate entity : serviceTemplate.getTopologyTemplate().getNodeTemplateOrRelationshipTemplate()) {
				if (entity instanceof TRelationshipTemplate) {
					foundRTs.add((TRelationshipTemplate) entity);
				}
			}

			return foundRTs;

		} catch (JAXBException e) {
			logger.error(e.getLocalizedMessage());
		}
		return null;

	}

	/**
	 * Turns XML Strings into {@link TEntityTemplate} objects using JaxB.
	 *
	 * @param xmlString
	 *            the XMLString to be parsed
	 * @return the parsed XMLString as {@link TEntityTemplate}
	 */
	public static List<TEntityTemplate> getEntityTemplatesAsJaxBObject(String xmlString) {
		try {
			StringReader reader = new StringReader(xmlString);

			Definitions jaxBDefinitions = (Definitions) createUnmarshaller().unmarshal(reader);
			TServiceTemplate serviceTemplate = (TServiceTemplate) jaxBDefinitions.getServiceTemplateOrNodeTypeOrNodeTypeImplementation().get(0);

			return serviceTemplate.getTopologyTemplate().getNodeTemplateOrRelationshipTemplate();

		} catch (JAXBException e) {
			logger.error(e.getLocalizedMessage());
		}
		return null;

	}

	/**
	 * Converts any object of the TOSCA data model to a JaxBObject.
	 *
	 * @param xmlString
	 *            the {@link Definitions} object to be converted
	 *
	 * @return the unmarshalled {@link Definitions} object
	 */
	public static Definitions getXasJaxBObject(String xmlString) {
		try {
			StringReader reader = new StringReader(xmlString);
			Definitions jaxBDefinitions = (Definitions) createUnmarshaller().unmarshal(reader);

			return jaxBDefinitions;

		} catch (JAXBException e) {
			logger.error(e.getLocalizedMessage());
		}
		return null;

	}

	/**
	 * This method adds a selection of {@link TNodeTemplate}- and {@link TRelationshipTemplate}-XML-Strings to a {@link TTopologyTemplate}-XML-String using JAXB.
	 * After the templates have been added, the {@link TTopologyTemplate} object is re-marshalled to an XML-String.
	 *
	 * This method is called by the selectionHandler.jsp after several Node or RelationshipTemplates have been chosen in a dialog.
	 *
	 * @param topology
	 *            the topology as XML string
	 * @param allTemplateChoicesAsXML
	 *            all possible template choices as TOSCA-XML strings containing the complete templates
	 * @param selectedNodeTemplatesAsJSON
	 *            the names of the selected NodeTemplates as JSONArray
	 * @param selectedRelationshipTemplatesAsJSON
	 *            the names of the selected RelationshipTemplates as JSONArray
	 *
	 * @return the complete topology XML string
	 */
	public static String addTemplatesToTopology(String topology, String allTemplateChoicesAsXML, String selectedNodeTemplatesAsJSON, String selectedRelationshipTemplatesAsJSON) {
		try {

			// initialization code for the jackson types used to convert JSON string arrays to a java.util.List
			ObjectMapper mapper = new ObjectMapper();
			TypeFactory factory = mapper.getTypeFactory();

			// convert the JSON array containing the names of the selected RelationshipTemplates to a java.util.List
			List<String> selectedRelationshipTemplates = mapper.readValue(selectedRelationshipTemplatesAsJSON, factory.constructCollectionType(List.class, String.class));

			// convert the topology and the choices to objects using JAXB
			TTopologyTemplate topologyTemplate = getTopologyAsJaxBObject(topology);
			List<TEntityTemplate> allTemplateChoices = getEntityTemplatesAsJaxBObject(allTemplateChoicesAsXML);

			// this distinction of cases is necessary because it is possible that only RelationshipTemplates have been selected
			if (selectedNodeTemplatesAsJSON != null) {

				// convert the JSON string array containing the names of the selected NodeTemplates to a java.util.List
				List<String> selectedNodeTemplates = mapper.readValue(selectedNodeTemplatesAsJSON, factory.constructCollectionType(List.class, String.class));

				// search the selected NodeTemplate in the List of all choices by its name to receive its object which will ne added to the topology
				for (String nodeTemplateName : selectedNodeTemplates) {
					for (TEntityTemplate choice : allTemplateChoices) {
						if (choice instanceof TNodeTemplate) {
							TNodeTemplate nodeTemplate = (TNodeTemplate) choice;
							// matching a name is usually unsafe because the uniqueness cannot be assured,
							// however similar names are not possible at this location due to the implementation of the selection dialogs
							if (nodeTemplateName.equals(nodeTemplate.getName())) {
								// add the selected NodeTemplate to the topology
								topologyTemplate.getNodeTemplateOrRelationshipTemplate().add(nodeTemplate);

								// due to the mapping of IDs in the selection dialog, the corresponding Relationship Template of the inserted Node Template misses its SourceElement.
								// Re-add it to avoid errors.
								for (TEntityTemplate entity: topologyTemplate.getNodeTemplateOrRelationshipTemplate()) {
									if (entity instanceof TRelationshipTemplate) {
										TRelationshipTemplate relationshipTemplate = (TRelationshipTemplate) entity;
										if (relationshipTemplate.getSourceElement().getRef() == null) {
											// connect to the added NodeTemplate
											SourceElement sourceElement = new SourceElement();
											sourceElement.setRef(nodeTemplate);
											relationshipTemplate.setSourceElement(sourceElement);
										}
									}
								}
							}
						}
					}
				}

				// now search and add the selected RelationshipTemplate object connecting to the inserted NodeTemplate
				for (String relationshipTemplateName : selectedRelationshipTemplates) {
					for (TEntityTemplate toBeAdded : allTemplateChoices) {
						if (toBeAdded instanceof TRelationshipTemplate) {
							TRelationshipTemplate relationshipTemplate = (TRelationshipTemplate) toBeAdded;
							if (relationshipTemplateName.equals(relationshipTemplate.getName())) {
								topologyTemplate.getNodeTemplateOrRelationshipTemplate().add(relationshipTemplate);
							}
						}
					}
				}

			} else {
				// in this case only Relationship Templates have been selected
				List<TRelationshipTemplate> allRelationshipTemplateChoices = JAXBHelper.getRelationshipTemplatesAsJaxBObject(allTemplateChoicesAsXML);

				// add the target Node Template to the topology which is unique due to the implementation of the selection dialog
				topologyTemplate.getNodeTemplateOrRelationshipTemplate().add((TNodeTemplate) ((TRelationshipTemplate) allRelationshipTemplateChoices.get(0)).getTargetElement().getRef());

				// search the JAXB object of the selected RelationshipTemplate and add it to the topology
				for (String relationshipTemplateName : selectedRelationshipTemplates) {
					for (TRelationshipTemplate choice : allRelationshipTemplateChoices) {
						if (relationshipTemplateName.equals(choice.getName())) {
							topologyTemplate.getNodeTemplateOrRelationshipTemplate().add(choice);
						}
					}
				}

				for (TEntityTemplate entityTemplate : topologyTemplate.getNodeTemplateOrRelationshipTemplate()) {
					if (entityTemplate instanceof TRelationshipTemplate) {
						TRelationshipTemplate relationship = (TRelationshipTemplate) entityTemplate;

						// due to the mapping of IDs in the selection dialog, the corresponding Relationship Template of the inserted Node Template misses its SourceElement.
						// Re-add it to avoid errors.
						if (relationship.getSourceElement().getRef() == null) {
							relationship.getSourceElement().setRef((TNodeTemplate) ((TRelationshipTemplate) allRelationshipTemplateChoices.get(0)).getTargetElement().getRef());
						}
					}
				}
			}

			// re-convert the topology from a JAXB object to an XML string and return it
			Definitions definitions = new Definitions();
			TServiceTemplate st = new TServiceTemplate();
			st.setTopologyTemplate(topologyTemplate);
			definitions.getServiceTemplateOrNodeTypeOrNodeTypeImplementation().add(st);
			JAXBContext context = JAXBContext.newInstance(Definitions.class);
			Marshaller m = context.createMarshaller();
			StringWriter stringWriter = new StringWriter();

			m.marshal(definitions, stringWriter);

			return stringWriter.toString();

		} catch (JAXBException | IOException e) {
			logger.error(e.getLocalizedMessage());
		}

		return null;
	}


	/**
	 * Marshalls a JAXB object of the TOSCA model to an XML string.
	 *
	 * @param clazz
	 * 			 the class of the object
	 * @param obj
	 * 			 the object to be marshalled
	 *
	 * @return
	 */
	public static String getXMLAsString(@SuppressWarnings("rawtypes") Class clazz, Object obj) {
		try {
			@SuppressWarnings("rawtypes")
			JAXBElement rootElement = Util.getJAXBElement(clazz, obj);
			JAXBContext context = JAXBContext.newInstance(TDefinitions.class);
			Marshaller m;

			m = context.createMarshaller();

			StringWriter w = new StringWriter();
			m.marshal(rootElement, w);
			String res = w.toString();

			return res;
		} catch (JAXBException e) {
			logger.error(e.getLocalizedMessage());
		}
		return null;
	}

	/**
	 * This methods alters the XML with JAXB so it can be imported in Winery. This is necessary because Winery needs additional information for the position of the templates in the
	 * Winery-Modeler-UI.
	 *
	 * This code is adapted from the org.eclipse.winery.repository.Utils.getXMLAsString() method.
	 *
	 * @param topology
	 *            the {@link TTopologyTemplate} to be altered
	 *
	 * @return the altered {@link TTopologyTemplate}
	 */
	public static TTopologyTemplate buildXML(TTopologyTemplate topology) {

		// the coordinate of the NodeTemplate in Winery. Begin 100 pixel from the top to improve arrangement.
		int yCoordinates = 100;

		for (TEntityTemplate template : topology.getNodeTemplateOrRelationshipTemplate()) {
			// add node templates
			if (template instanceof TNodeTemplate) {

				TNodeTemplate nodeTemplate = (TNodeTemplate) template;

				// remove the Requirements tag if necessary
				if (nodeTemplate.getRequirements() != null && nodeTemplate.getRequirements().getRequirement() == null) {
					nodeTemplate.setRequirements(null);
				}

				ModelUtilities.setLeft(nodeTemplate, NODETEMPLATE_X_COORDINATE);
				ModelUtilities.setTop(nodeTemplate, Integer.toString(yCoordinates));

				yCoordinates += 150;
			}
		}

		return topology;
	}
}