1 /*******************************************************************************
2 * Copyright (c) 2013 University of Stuttgart.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * and the Apache License 2.0 which both accompany this distribution,
6 * and are available at http://www.eclipse.org/legal/epl-v10.html
7 * and http://www.apache.org/licenses/LICENSE-2.0
10 * Oliver Kopp - initial API and implementation
11 *******************************************************************************/
12 package org.eclipse.winery.common;
14 import java.lang.reflect.Method;
15 import java.util.Iterator;
17 import java.util.Properties;
18 import java.util.UUID;
20 import javax.xml.XMLConstants;
21 import javax.xml.namespace.QName;
22 import javax.xml.parsers.DocumentBuilder;
23 import javax.xml.parsers.DocumentBuilderFactory;
24 import javax.xml.parsers.ParserConfigurationException;
26 import org.apache.commons.lang3.StringUtils;
27 import org.eclipse.winery.common.constants.Namespaces;
28 import org.eclipse.winery.common.constants.QNames;
29 import org.eclipse.winery.common.propertydefinitionkv.PropertyDefinitionKV;
30 import org.eclipse.winery.common.propertydefinitionkv.PropertyDefinitionKVList;
31 import org.eclipse.winery.common.propertydefinitionkv.WinerysPropertiesDefinition;
32 import org.eclipse.winery.model.tosca.TBoundaryDefinitions;
33 import org.eclipse.winery.model.tosca.TCapability;
34 import org.eclipse.winery.model.tosca.TCapabilityDefinition;
35 import org.eclipse.winery.model.tosca.TEntityTemplate;
36 import org.eclipse.winery.model.tosca.TEntityType;
37 import org.eclipse.winery.model.tosca.TExtensibleElements;
38 import org.eclipse.winery.model.tosca.TNodeTemplate;
39 import org.eclipse.winery.model.tosca.TNodeTemplate.Capabilities;
40 import org.eclipse.winery.model.tosca.TNodeTemplate.Requirements;
41 import org.eclipse.winery.model.tosca.TNodeType;
42 import org.eclipse.winery.model.tosca.TPlan;
43 import org.eclipse.winery.model.tosca.TPlans;
44 import org.eclipse.winery.model.tosca.TRelationshipTemplate;
45 import org.eclipse.winery.model.tosca.TRelationshipTemplate.SourceElement;
46 import org.eclipse.winery.model.tosca.TRelationshipTemplate.TargetElement;
47 import org.eclipse.winery.model.tosca.TRelationshipType;
48 import org.eclipse.winery.model.tosca.TRequirement;
49 import org.eclipse.winery.model.tosca.TRequirementDefinition;
50 import org.eclipse.winery.model.tosca.TServiceTemplate;
51 import org.eclipse.winery.model.tosca.TTopologyTemplate;
52 import org.slf4j.LoggerFactory;
53 import org.w3c.dom.Comment;
54 import org.w3c.dom.Document;
55 import org.w3c.dom.Element;
56 import org.w3c.dom.Node;
57 import org.w3c.dom.NodeList;
58 import org.w3c.dom.Text;
60 public class ModelUtilities {
62 private static final org.slf4j.Logger logger = LoggerFactory.getLogger(ModelUtilities.class);
66 * This is a special method for Winery. Winery allows to define a property
67 * definition by specifying name/type values. Instead of parsing the
68 * extensible elements returned TDefinitions, this method is a convenience
69 * method to access this information
71 * @param t the entitytype to read the properties definition from
72 * @return a WinerysPropertiesDefinition object, which includes a map of
73 * name/type-pairs denoting the associated property definitions. A
74 * default element name and namespace is added if it is not defined
75 * in the underlying XML. null if no Winery specific KV properties
76 * are defined for the given entity type
78 public static WinerysPropertiesDefinition getWinerysPropertiesDefinition(TEntityType et) {
79 // similar implementation as org.eclipse.winery.repository.resources.entitytypes.properties.PropertiesDefinitionResource.getListFromEntityType(TEntityType)
80 WinerysPropertiesDefinition res = null;
81 for (Object o : et.getAny()) {
82 if (o instanceof WinerysPropertiesDefinition) {
83 res = (WinerysPropertiesDefinition) o;
88 // we put defaults if elementname and namespace have not been set
90 if (res.getElementName() == null) {
91 res.setElementName("Properties");
94 if (res.getNamespace() == null) {
95 // we use the targetnamespace of the original element
96 String ns = et.getTargetNamespace();
97 if (!ns.endsWith("/")) {
100 ns += "propertiesdefinition/winery";
101 res.setNamespace(ns);
109 * This is a special method for Winery. Winery allows to define a property
110 * by specifying name/value values. Instead of parsing the XML contained in
111 * TNodeType, this method is a convenience method to access this information
113 * The return type "Properties" is used because of the key/value properties.
115 * @param template the node template to get the associated properties
117 public static Properties getPropertiesKV(TEntityTemplate template) {
118 Properties properties = new Properties();
119 org.eclipse.winery.model.tosca.TEntityTemplate.Properties tprops = template.getProperties();
120 if (tprops != null) {
121 // no checking for validity, just reading
122 Element el = (Element) tprops.getAny();
124 // somehow invalid .tosca. We return empty properties instead of throwing a NPE
127 NodeList childNodes = el.getChildNodes();
128 for (int i = 0; i < childNodes.getLength(); i++) {
129 Node item = childNodes.item(i);
130 if (item instanceof Element) {
131 String key = item.getLocalName();
132 String value = item.getTextContent();
133 properties.put(key, value);
141 * This is a special method for Winery. Winery allows to define a property
142 * by specifying name/value values. We convert the given Properties to XML.
144 * @param wpd the Winery's properties definition of the type of the given
145 * template (i.e., wpd =
146 * getWinerysPropertiesDefinition(template.getType()))
147 * @param template the node template to set the associated properties
149 public static void setPropertiesKV(WinerysPropertiesDefinition wpd, TEntityTemplate template, Properties properties) {
150 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
153 db = dbf.newDocumentBuilder();
154 } catch (ParserConfigurationException e) {
155 ModelUtilities.logger.debug(e.getMessage(), e);
156 throw new IllegalStateException("Could not instantiate document builder", e);
158 Document doc = db.newDocument();
160 Element root = doc.createElementNS(wpd.getNamespace(), wpd.getElementName());
161 doc.appendChild(root);
163 // we produce the serialization in the same order the XSD would be generated (because of the usage of xsd:sequence)
164 for (PropertyDefinitionKV prop : wpd.getPropertyDefinitionKVList()) {
165 // we always write the element tag as the XSD forces that
166 Element element = doc.createElementNS(wpd.getNamespace(), prop.getKey());
167 root.appendChild(element);
168 String value = properties.getProperty(prop.getKey());
170 Text text = doc.createTextNode(value);
171 element.appendChild(text);
175 org.eclipse.winery.model.tosca.TEntityTemplate.Properties tprops = new org.eclipse.winery.model.tosca.TEntityTemplate.Properties();
176 tprops.setAny(doc.getDocumentElement());
177 template.setProperties(tprops);
181 * Generates a XSD when Winery's K/V properties are used. This method is put
182 * here instead of WinerysPropertiesDefinitionResource to avoid generating
185 * public because of the usage by TOSCAEXportUtil
187 * @return empty Document, if Winery's Properties Definition is not fully
188 * filled (e.g., no wrapping element defined)
190 public static Document getWinerysPropertiesDefinitionXSDAsDocument(WinerysPropertiesDefinition wpd) {
192 * This is a quick hack: an XML schema container is created for each
193 * element. Smarter solution: create a hash from namespace to XML schema
194 * element and re-use that for each new element
195 * Drawback of "smarter" solution: not a single XSD file any more
197 DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
198 DocumentBuilder docBuilder;
200 docBuilder = docFactory.newDocumentBuilder();
201 } catch (ParserConfigurationException e) {
202 ModelUtilities.logger.debug(e.getMessage(), e);
203 throw new IllegalStateException("Could not instantiate document builder", e);
205 Document doc = docBuilder.newDocument();
207 if (!ModelUtilities.allRequiredFieldsNonNull(wpd)) {
208 // wpd not fully filled -> valid XSD cannot be provided
209 // fallback: add comment and return "empty" document
210 Comment comment = doc.createComment("Required fields are missing in Winery's key/value properties definition.");
211 doc.appendChild(comment);
215 // create XSD schema container
216 Element schemaElement = doc.createElementNS(XMLConstants.W3C_XML_SCHEMA_NS_URI, "schema");
217 doc.appendChild(schemaElement);
218 schemaElement.setAttribute("elementFormDefault", "qualified");
219 schemaElement.setAttribute("attributeFormDefault", "unqualified");
220 schemaElement.setAttribute("targetNamespace", wpd.getNamespace());
222 // create XSD element itself
223 Element el = doc.createElementNS(XMLConstants.W3C_XML_SCHEMA_NS_URI, "element");
224 schemaElement.appendChild(el);
225 el.setAttribute("name", wpd.getElementName());
226 Element el2 = doc.createElementNS(XMLConstants.W3C_XML_SCHEMA_NS_URI, "complexType");
229 el2 = doc.createElementNS(XMLConstants.W3C_XML_SCHEMA_NS_URI, "sequence");
233 // currently, "xsd" is a hardcoded prefix in the type definition
234 el.setAttribute("xmlns:xsd", XMLConstants.W3C_XML_SCHEMA_NS_URI);
236 for (PropertyDefinitionKV prop : wpd.getPropertyDefinitionKVList()) {
237 el2 = doc.createElementNS(XMLConstants.W3C_XML_SCHEMA_NS_URI, "element");
239 el2.setAttribute("name", prop.getKey());
240 // prop.getType has the prefix included
241 el2.setAttribute("type", prop.getType());
248 * Removes an existing Winery's Properties definition. If no such definition
249 * exists, the TEntityType is not modified
251 public static void removeWinerysPropertiesDefinition(TEntityType et) {
252 for (Iterator<Object> iterator = et.getAny().iterator(); iterator.hasNext();) {
253 Object o = iterator.next();
254 if (o instanceof WinerysPropertiesDefinition) {
261 public static void replaceWinerysPropertiesDefinition(TEntityType et, WinerysPropertiesDefinition wpd) {
262 ModelUtilities.removeWinerysPropertiesDefinition(et);
263 et.getAny().add(wpd);
266 public static String getBorderColor(TNodeType nt) {
267 String borderColor = nt.getOtherAttributes().get(QNames.QNAME_BORDER_COLOR);
268 if (borderColor == null) {
269 borderColor = Util.getColor(nt.getName());
274 public static String getColor(TRelationshipType rt) {
275 String color = rt.getOtherAttributes().get(QNames.QNAME_COLOR);
277 color = Util.getColor(rt.getName());
283 * Returns the Properties. If no properties exist, the element is created
287 public static org.eclipse.winery.model.tosca.TBoundaryDefinitions.Properties getProperties(TBoundaryDefinitions defs) {
288 org.eclipse.winery.model.tosca.TBoundaryDefinitions.Properties properties = defs.getProperties();
289 if (properties == null) {
290 properties = new org.eclipse.winery.model.tosca.TBoundaryDefinitions.Properties();
291 defs.setProperties(properties);
297 * Special method to get the name of an extensible element as the TOSCA
298 * specification does not have a separate super type for elements with a
302 * org.eclipse.winery.common.Util.instanceSupportsNameAttribute(Class<?
303 * extends TOSCAComponentId>)} is related
305 * @param e the extensible element offering a name attribute (besides an id
307 * @return the name of the extensible element
308 * @throws IllegalStateException if e does not offer the method "getName"
310 public static String getName(TExtensibleElements e) {
314 method = e.getClass().getMethod("getName");
315 res = method.invoke(e);
316 } catch (Exception ex) {
317 throw new IllegalStateException(ex);
323 * Returns the name of the given element. If the name does not exist or is
324 * empty, the id is returned
328 * @return the name if there is a name field, if not, the id is returned. In
329 * case there is a Name field,
331 public static String getNameWithIdFallBack(TExtensibleElements ci) {
335 method = ci.getClass().getMethod("getName");
336 res = (String) method.invoke(ci);
337 } catch (Exception e) {
339 if (StringUtils.isEmpty(res)) {
341 method = ci.getClass().getMethod("getId");
342 res = (String) method.invoke(ci);
343 } catch (Exception e2) {
344 throw new IllegalStateException(e2);
351 * Special method to set the name of an extensible element as the TOSCA
352 * specification does not have a separate super type for elements with a
355 * @param e the extensible element offering a name attribute (besides an id
357 * @param name the new name
358 * @throws IllegalStateException if e does not offer the method "getName"
360 public static void setName(TExtensibleElements e, String name) {
363 method = e.getClass().getMethod("setName", String.class);
364 method.invoke(e, name);
365 } catch (Exception ex) {
366 throw new IllegalStateException(ex);
370 public static boolean allRequiredFieldsNonNull(WinerysPropertiesDefinition wpd) {
371 boolean valid = wpd.getNamespace() != null;
372 valid = valid && (wpd.getElementName() != null);
374 PropertyDefinitionKVList propertyDefinitionKVList = wpd.getPropertyDefinitionKVList();
375 valid = (propertyDefinitionKVList != null);
377 for (PropertyDefinitionKV def : propertyDefinitionKVList) {
378 valid = valid && (def.getKey() != null);
379 valid = valid && (def.getType() != null);
387 * @return null if no explicit left is set
389 public static String getLeft(TNodeTemplate nodeTemplate) {
390 Map<QName, String> otherAttributes = nodeTemplate.getOtherAttributes();
391 String left = otherAttributes.get(new QName(Namespaces.TOSCA_WINERY_EXTENSIONS_NAMESPACE, "x"));
396 * @return null if no explicit left is set
398 public static String getTop(TNodeTemplate nodeTemplate) {
399 Map<QName, String> otherAttributes = nodeTemplate.getOtherAttributes();
400 String top = otherAttributes.get(new QName(Namespaces.TOSCA_WINERY_EXTENSIONS_NAMESPACE, "y"));
405 * locates targetObjectRef inside a topology template
407 * @param topologyTemplate the topology template to search in
408 * @param targetObjectRef the object ref as String
410 * @return null if not found, otherwise the entity template in the topology
412 public static TEntityTemplate findNodeTemplateOrRequirementOfNodeTemplateOrCapabilityOfNodeTemplateOrRelationshipTemplate(TTopologyTemplate topologyTemplate, String targetObjectRef) {
413 // We cannot use XMLs id pointing capabilities as we work on the Java model
414 // Other option: modify the stored XML directly. This is more error prune than walking through the whole topology
415 for (TEntityTemplate t : topologyTemplate.getNodeTemplateOrRelationshipTemplate()) {
416 if (t instanceof TNodeTemplate) {
417 if (t.getId().equals(targetObjectRef)) {
420 TNodeTemplate nt = (TNodeTemplate) t;
422 Requirements requirements = nt.getRequirements();
423 if (requirements != null) {
424 for (TRequirement req : requirements.getRequirement()) {
425 if (req.getId().equals(targetObjectRef)) {
431 Capabilities capabilities = nt.getCapabilities();
432 if (capabilities != null) {
433 for (TCapability cap : capabilities.getCapability()) {
434 if (cap.getId().equals(targetObjectRef)) {
441 assert (t instanceof TRelationshipTemplate);
442 if (t.getId().equals(targetObjectRef)) {
448 // no return hit inside the loop: nothing was found
453 * Returns the id of the given element
455 * The TOSCA specification does NOT always put an id field. In the case of
456 * EntityTypes and EntityTypeImplementations, there is no id, but a name
459 * This method abstracts from that fact.
461 public static String getId(TExtensibleElements ci) {
465 method = ci.getClass().getMethod("getId");
466 res = method.invoke(ci);
467 } catch (Exception e) {
468 // If no "getId" method is there, we try "getName"
470 method = ci.getClass().getMethod("getName");
471 res = method.invoke(ci);
472 } catch (Exception e2) {
473 throw new IllegalStateException(e2);
480 * Resolves a given id as requirement in the given ServiceTemplate
482 * @return null if not found
484 public static TRequirement resolveRequirement(TServiceTemplate serviceTemplate, String reference) {
485 TRequirement resolved = null;
486 for (TEntityTemplate tmpl : serviceTemplate.getTopologyTemplate().getNodeTemplateOrRelationshipTemplate()) {
487 if (tmpl instanceof TNodeTemplate) {
488 TNodeTemplate n = (TNodeTemplate) tmpl;
489 Requirements requirements = n.getRequirements();
490 if (requirements != null) {
491 for (TRequirement req : n.getRequirements().getRequirement()) {
492 if (req.getId().equals(reference)) {
502 public static TCapability resolveCapability(TServiceTemplate serviceTemplate, String reference) {
503 TCapability resolved = null;
504 for (TEntityTemplate tmpl : serviceTemplate.getTopologyTemplate().getNodeTemplateOrRelationshipTemplate()) {
505 if (tmpl instanceof TNodeTemplate) {
506 TNodeTemplate n = (TNodeTemplate) tmpl;
507 Capabilities capabilities = n.getCapabilities();
508 if (capabilities != null) {
509 for (TCapability cap : n.getCapabilities().getCapability()) {
510 if (cap.getId().equals(reference)) {
520 public static TNodeTemplate resolveNodeTemplate(TServiceTemplate serviceTemplate, String reference) {
521 TNodeTemplate resolved = null;
522 for (TEntityTemplate tmpl : serviceTemplate.getTopologyTemplate().getNodeTemplateOrRelationshipTemplate()) {
523 if (tmpl instanceof TNodeTemplate) {
524 TNodeTemplate n = (TNodeTemplate) tmpl;
525 if (n.getId().equals(reference)) {
533 public static TRelationshipTemplate resolveRelationshipTemplate(TServiceTemplate serviceTemplate, String reference) {
534 TRelationshipTemplate resolved = null;
535 for (TEntityTemplate tmpl : serviceTemplate.getTopologyTemplate().getNodeTemplateOrRelationshipTemplate()) {
536 if (tmpl instanceof TRelationshipTemplate) {
537 TRelationshipTemplate n = (TRelationshipTemplate) tmpl;
538 if (n.getId().equals(reference)) {
546 public static TPlan resolvePlan(TServiceTemplate serviceTemplate, String reference) {
547 TPlan resolved = null;
548 TPlans plans = serviceTemplate.getPlans();
552 for (TPlan p : plans.getPlan()) {
553 if (p.getId().equals(reference)) {
561 * Sets the x coordinate of a {@link TNodeTemplate}.
563 * @param nodeTemplate
564 * the nodeTemplate to be altered
566 * the value of the coordinate to be set
568 * the altered {@link TNodeTemplate}
570 public static TNodeTemplate setLeft(TNodeTemplate nodeTemplate, String coordinate) {
572 Map<QName, String> otherNodeTemplateAttributes = nodeTemplate.getOtherAttributes();
573 otherNodeTemplateAttributes.put(new QName(Namespaces.TOSCA_WINERY_EXTENSIONS_NAMESPACE, "x"), coordinate);
579 * Sets the y coordinate of a {@link TNodeTemplate}.
581 * @param nodeTemplate
582 * the nodeTemplate to be altered
584 * the value of the coordinate to be set
586 * the altered {@link TNodeTemplate}
588 public static TNodeTemplate setTop(TNodeTemplate nodeTemplate, String coordinate) {
590 Map<QName, String> otherNodeTemplateAttributes = nodeTemplate.getOtherAttributes();
591 otherNodeTemplateAttributes.put(new QName(Namespaces.TOSCA_WINERY_EXTENSIONS_NAMESPACE, "y"), coordinate);
598 * This method instantiates a {@link TNodeTemplate} for a given {@link TNodeType}.
601 * the {@link TNodeType} used for the {@link TNodeTemplate} instantiation.
603 * @return the instantiated {@link TNodeTemplate}
605 public static TNodeTemplate instantiateNodeTemplate(TNodeType nodeType) {
607 TNodeTemplate nodeTemplate = new TNodeTemplate();
609 nodeTemplate.setId(UUID.randomUUID().toString());
610 nodeTemplate.setName(nodeType.getName());
611 nodeTemplate.setType(new QName(nodeType.getTargetNamespace(), nodeType.getName()));
613 // add capabilities to the NodeTemplate
614 if (nodeType.getCapabilityDefinitions() != null) {
615 for (TCapabilityDefinition cd : nodeType.getCapabilityDefinitions().getCapabilityDefinition()) {
616 TCapability capa = new TCapability();
617 capa.setId(UUID.randomUUID().toString());
618 capa.setName(cd.getCapabilityType().getLocalPart());
619 capa.setType(new QName(cd.getCapabilityType().getNamespaceURI(), cd.getCapabilityType().getLocalPart()));
620 nodeTemplate.setCapabilities(new Capabilities());
621 nodeTemplate.getCapabilities().getCapability().add(capa);
626 if (nodeType.getRequirementDefinitions() != null && nodeType.getRequirementDefinitions().getRequirementDefinition() != null) {
627 Requirements requirementsNode = new Requirements();
628 nodeTemplate.setRequirements(requirementsNode);
629 for (TRequirementDefinition definition : nodeType.getRequirementDefinitions().getRequirementDefinition()) {
630 TRequirement newRequirement = new TRequirement();
631 newRequirement.setName(definition.getName());
632 newRequirement.setId(definition.getName());
633 newRequirement.setType(definition.getRequirementType());
634 nodeTemplate.getRequirements().getRequirement().add(newRequirement);
642 * This method instantiates a {@link TRelationshipTemplate} for a given {@link TRelationshipType}.
645 * the {@link TRelationshipType} used for the {@link TRelationshipTemplate} instantiation.
646 * @param sourceNodeTemplate
647 * the source {@link TNodeTemplate} of the connection
648 * @param targetNodeTemplate
649 * the target {@link TNodeTemplate} of the connection
651 * @return the instantiated {@link TRelationshipTemplate}
653 public static TRelationshipTemplate instantiateRelationshipTemplate(TRelationshipType relationshipType, TNodeTemplate sourceNodeTemplate, TNodeTemplate targetNodeTemplate) {
655 TRelationshipTemplate relationshipTemplate = new TRelationshipTemplate();
656 relationshipTemplate.setId(UUID.randomUUID().toString());
657 relationshipTemplate.setName(relationshipType.getName());
658 relationshipTemplate.setType(new QName(relationshipType.getTargetNamespace(), relationshipType.getName()));
660 // connect the NodeTemplates
661 SourceElement source = new SourceElement();
662 source.setRef(sourceNodeTemplate);
663 relationshipTemplate.setSourceElement(source);
664 TargetElement target = new TargetElement();
665 target.setRef(targetNodeTemplate);
666 relationshipTemplate.setTargetElement(target);
668 return relationshipTemplate;