Skip to content

Commit f2fbee3

Browse files
committed
Add initial implementation
Signed-off-by: Vladimir Yussupov <v.yussupov@gmail.com>
1 parent 8f93851 commit f2fbee3

File tree

12 files changed

+475
-23
lines changed

12 files changed

+475
-23
lines changed

org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/TBoundaryDefinitions.java

Lines changed: 153 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,31 @@
1515
package org.eclipse.winery.model.tosca;
1616

1717
import java.util.ArrayList;
18+
import java.util.LinkedHashMap;
1819
import java.util.List;
20+
import java.util.Map;
1921
import java.util.Objects;
2022

2123
import javax.xml.bind.annotation.XmlAccessType;
2224
import javax.xml.bind.annotation.XmlAccessorType;
2325
import javax.xml.bind.annotation.XmlAnyElement;
2426
import javax.xml.bind.annotation.XmlElement;
2527
import javax.xml.bind.annotation.XmlType;
28+
import javax.xml.parsers.DocumentBuilder;
29+
import javax.xml.parsers.DocumentBuilderFactory;
30+
import javax.xml.parsers.ParserConfigurationException;
2631

32+
import org.eclipse.winery.model.tosca.constants.Namespaces;
33+
34+
import io.github.adr.embedded.ADR;
2735
import org.eclipse.jdt.annotation.NonNull;
2836
import org.eclipse.jdt.annotation.Nullable;
37+
import org.w3c.dom.Comment;
38+
import org.w3c.dom.Document;
39+
import org.w3c.dom.Element;
40+
import org.w3c.dom.Node;
41+
import org.w3c.dom.NodeList;
42+
import org.w3c.dom.Text;
2943

3044
@XmlAccessorType(XmlAccessType.FIELD)
3145
@XmlType(name = "tBoundaryDefinitions", propOrder = {
@@ -197,13 +211,151 @@ public static class Properties {
197211

198212
@Nullable
199213
public Object getAny() {
200-
return any;
214+
if (this.getKVProperties() == null) {
215+
return any;
216+
} else {
217+
return null;
218+
}
201219
}
202220

203221
public void setAny(@Nullable Object value) {
204222
this.any = value;
205223
}
206224

225+
/**
226+
* This is a special method for Winery. Winery allows to define a property by specifying name/value values.
227+
* Instead of parsing the XML contained in TNodeType, this method is a convenience method to access this
228+
* information assumes the properties are key/value pairs (see WinerysPropertiesDefinition), all other cases are
229+
* return null.
230+
* <p>
231+
* Returns a map of key/values of this template based on the information of WinerysPropertiesDefinition. In case
232+
* no value is set, the empty string is used. The map is implemented as {@link LinkedHashMap} to ensure that the
233+
* order of the elements is the same as in the XML. We return the type {@link LinkedHashMap}, because there is
234+
* no appropriate Java interface for "sorted" Maps
235+
* <p>
236+
* In case the element is not of the form k/v, null is returned
237+
* <p>
238+
* This method assumes that the any field is always populated.
239+
*
240+
* @return null if not k/v, a map of k/v properties otherwise
241+
*/
242+
@ADR(12)
243+
public LinkedHashMap<String, String> getKVProperties() {
244+
// we use the internal variable "any", because getAny() returns null, if we have KVProperties
245+
if (any == null || !(any instanceof Element)) {
246+
return null;
247+
}
248+
249+
Element el = (Element) any;
250+
if (el == null) {
251+
return null;
252+
}
253+
254+
// we have no type information in this place
255+
// we could inject a repository, but if Winery is used with multiple repositories, this could cause race conditions
256+
// therefore, we guess at the instance of the properties definition (i.e., here) if it is key/value or not.
257+
258+
boolean isKv = true;
259+
260+
LinkedHashMap<String, String> properties = new LinkedHashMap<>();
261+
262+
NodeList childNodes = el.getChildNodes();
263+
264+
if (childNodes.getLength() == 0) {
265+
// somehow invalid XML - do not treat it as k/v
266+
return null;
267+
}
268+
269+
for (int i = 0; i < childNodes.getLength(); i++) {
270+
Node item = childNodes.item(i);
271+
if (item instanceof Element) {
272+
String key = item.getLocalName();
273+
String value;
274+
275+
Element kvElement = (Element) item;
276+
NodeList kvElementChildNodes = kvElement.getChildNodes();
277+
if (kvElementChildNodes.getLength() == 0) {
278+
value = "";
279+
} else if (kvElementChildNodes.getLength() > 1) {
280+
// This is a wrong guess if comments are used, but this is prototype
281+
isKv = false;
282+
break;
283+
} else {
284+
// one child - just get the text.
285+
value = item.getTextContent();
286+
}
287+
properties.put(key, value);
288+
} else if (item instanceof Text || item instanceof Comment) {
289+
// these kinds of nodes are OK
290+
}
291+
}
292+
293+
if (isKv) {
294+
return properties;
295+
} else {
296+
return null;
297+
}
298+
}
299+
300+
@ADR(12)
301+
public void setKVProperties(Map<String, String> properties) {
302+
Objects.requireNonNull(properties);
303+
Element el = (Element) any;
304+
305+
if (el == null) {
306+
// special case if JSON is parsed without updating an existing element
307+
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
308+
DocumentBuilder db;
309+
try {
310+
db = dbf.newDocumentBuilder();
311+
} catch (ParserConfigurationException e) {
312+
throw new IllegalStateException("Could not instantiate document builder", e);
313+
}
314+
Document doc = db.newDocument();
315+
316+
// We cannot access the wrapper definitions, because we don't have access to the type
317+
// Element root = doc.createElementNS(wpd.getNamespace(), wpd.getElementName());
318+
// Therefore, we create a dummy wrapper element:
319+
320+
Element root = doc.createElementNS(Namespaces.EXAMPLE_NAMESPACE_URI, "Properties");
321+
doc.appendChild(root);
322+
323+
// No wpd - so this is not possible:
324+
// we produce the serialization in the same order the XSD would be generated (because of the usage of xsd:sequence)
325+
// for (PropertyDefinitionKV prop : wpd.getPropertyDefinitionKVList()) {
326+
327+
for (String key : properties.keySet()) {
328+
// wpd.getNamespace()
329+
Element element = doc.createElementNS(Namespaces.EXAMPLE_NAMESPACE_URI, key);
330+
root.appendChild(element);
331+
String value = properties.get(key);
332+
if (value != null) {
333+
Text text = doc.createTextNode(value);
334+
element.appendChild(text);
335+
}
336+
}
337+
338+
this.setAny(doc.getDocumentElement());
339+
} else {
340+
// straight-forward copy over to existing property structure
341+
NodeList childNodes = el.getChildNodes();
342+
343+
for (int i = 0; i < childNodes.getLength(); i++) {
344+
Node item = childNodes.item(i);
345+
if (item instanceof Element) {
346+
final Element element = (Element) item;
347+
final String key = element.getLocalName();
348+
final String value = properties.get(key);
349+
if (value != null) {
350+
element.setTextContent(value);
351+
}
352+
} else if (item instanceof Text || item instanceof Comment) {
353+
// these kinds of nodes are OK
354+
}
355+
}
356+
}
357+
}
358+
207359
public TBoundaryDefinitions.Properties.@Nullable PropertyMappings getPropertyMappings() {
208360
return propertyMappings;
209361
}

org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/TServiceTemplate.java

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,34 @@
2121
import javax.xml.bind.annotation.XmlAccessorType;
2222
import javax.xml.bind.annotation.XmlAttribute;
2323
import javax.xml.bind.annotation.XmlElement;
24+
import javax.xml.bind.annotation.XmlElements;
2425
import javax.xml.bind.annotation.XmlSchemaType;
26+
import javax.xml.bind.annotation.XmlTransient;
2527
import javax.xml.bind.annotation.XmlType;
2628
import javax.xml.namespace.QName;
2729

30+
import org.eclipse.winery.model.tosca.constants.Namespaces;
31+
import org.eclipse.winery.model.tosca.kvproperties.WinerysPropertiesDefinition;
32+
33+
import com.fasterxml.jackson.annotation.JsonIgnore;
2834
import org.eclipse.jdt.annotation.Nullable;
2935

3036
@XmlAccessorType(XmlAccessType.FIELD)
3137
@XmlType(name = "tServiceTemplate", propOrder = {
38+
"propertiesDefinition",
3239
"tags",
3340
"boundaryDefinitions",
3441
"topologyTemplate",
3542
"plans"
3643
})
3744
public class TServiceTemplate extends HasId implements HasName, HasTargetNamespace {
45+
public static final String NS_SUFFIX_PROPERTIESDEFINITION_WINERY = "propertiesdefinition/winery";
46+
47+
@XmlElements( {
48+
@XmlElement(name = "PropertiesDefinition", namespace = Namespaces.TOSCA_WINERY_EXTENSIONS_NAMESPACE, type = TServiceTemplate.PropertiesDefinition.class),
49+
@XmlElement(name = "PropertiesDefinition", namespace = Namespaces.TOSCA_WINERY_EXTENSIONS_NAMESPACE, type = WinerysPropertiesDefinition.class)
50+
})
51+
protected Object propertiesDefinition;
3852

3953
@XmlElement(name = "Tags")
4054
protected TTags tags;
@@ -158,6 +172,74 @@ public void setSubstitutableNodeType(QName value) {
158172
this.substitutableNodeType = value;
159173
}
160174

175+
public Object getPropertiesDefinition() {
176+
return propertiesDefinition;
177+
}
178+
179+
public void setPropertiesDefinition(Object value) {
180+
this.propertiesDefinition = value;
181+
}
182+
183+
/**
184+
* <p>Java class for anonymous complex type.
185+
* <p>
186+
* <p>The following schema fragment specifies the expected content contained within this class.
187+
* <p>
188+
* <pre>
189+
* &lt;complexType>
190+
* &lt;complexContent>
191+
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
192+
* &lt;attribute name="element" type="{http://www.w3.org/2001/XMLSchema}QName" />
193+
* &lt;attribute name="type" type="{http://www.w3.org/2001/XMLSchema}QName" />
194+
* &lt;/restriction>
195+
* &lt;/complexContent>
196+
* &lt;/complexType>
197+
* </pre>
198+
*/
199+
@XmlAccessorType(XmlAccessType.FIELD)
200+
@XmlType(name = "")
201+
public static class PropertiesDefinition extends TEntityType.PropertiesDefinition {
202+
}
203+
204+
/**
205+
* This is a special method for Winery. Winery allows to define a property definition by specifying name/type
206+
* values. Instead of parsing the extensible elements returned TDefinitions, this method is a convenience method to
207+
* access this information
208+
*
209+
* @return a WinerysPropertiesDefinition object, which includes a map of name/type-pairs denoting the associated
210+
* property definitions. A default element name and namespace is added if it is not defined in the underlying XML.
211+
* null if no Winery specific KV properties are defined for the given entity type
212+
*/
213+
@XmlTransient
214+
@JsonIgnore
215+
public WinerysPropertiesDefinition getWinerysPropertiesDefinition() {
216+
// similar implementation as org.eclipse.winery.repository.resources.entitytypes.properties.PropertiesDefinitionResource.getListFromEntityType(TEntityType)
217+
WinerysPropertiesDefinition res = null;
218+
if (this.getPropertiesDefinition() instanceof WinerysPropertiesDefinition) {
219+
res = (WinerysPropertiesDefinition) this.getPropertiesDefinition();
220+
}
221+
222+
if (res != null) {
223+
// we put defaults if elementname and namespace have not been set
224+
225+
if (res.getElementName() == null) {
226+
res.setElementName("Properties");
227+
}
228+
229+
if (res.getNamespace() == null) {
230+
// we use the targetnamespace of the original element
231+
String ns = this.getTargetNamespace();
232+
if (!ns.endsWith("/")) {
233+
ns += "/";
234+
}
235+
ns += NS_SUFFIX_PROPERTIESDEFINITION_WINERY;
236+
res.setNamespace(ns);
237+
}
238+
}
239+
240+
return res;
241+
}
242+
161243
public static class Builder extends HasId.Builder<Builder> {
162244
private final TTopologyTemplate topologyTemplate;
163245

org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/apiData/PropertiesDefinitionResourceApiData.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,23 @@ public PropertiesDefinitionResourceApiData(
4343
this.selectedValue = PropertiesDefinitionEnum.None;
4444
}
4545
}
46+
47+
public PropertiesDefinitionResourceApiData(Object propertiesDefinition) {
48+
if (propertiesDefinition instanceof TEntityType.PropertiesDefinition) {
49+
this.propertiesDefinition = (TEntityType.PropertiesDefinition) propertiesDefinition;
50+
}
51+
if (propertiesDefinition instanceof WinerysPropertiesDefinition) {
52+
this.winerysPropertiesDefinition = (WinerysPropertiesDefinition) propertiesDefinition;
53+
}
54+
55+
if ((winerysPropertiesDefinition != null) && (winerysPropertiesDefinition.getIsDerivedFromXSD() == null)) {
56+
this.selectedValue = PropertiesDefinitionEnum.Custom;
57+
} else if ((this.propertiesDefinition != null) && (this.propertiesDefinition.getElement() != null)) {
58+
this.selectedValue = PropertiesDefinitionEnum.Element;
59+
} else if ((this.propertiesDefinition != null) && (this.propertiesDefinition.getType() != null)) {
60+
this.selectedValue = PropertiesDefinitionEnum.Type;
61+
} else {
62+
this.selectedValue = PropertiesDefinitionEnum.None;
63+
}
64+
}
4665
}

org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/ServiceTemplateResource.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,10 @@
5252
import org.eclipse.winery.repository.rest.resources._support.IHasName;
5353
import org.eclipse.winery.repository.rest.resources._support.dataadapter.injectionadapter.InjectorReplaceData;
5454
import org.eclipse.winery.repository.rest.resources._support.dataadapter.injectionadapter.InjectorReplaceOptions;
55+
import org.eclipse.winery.repository.rest.resources.apiData.PropertiesDefinitionResourceApiData;
5556
import org.eclipse.winery.repository.rest.resources.servicetemplates.boundarydefinitions.BoundaryDefinitionsResource;
5657
import org.eclipse.winery.repository.rest.resources.servicetemplates.plans.PlansResource;
58+
import org.eclipse.winery.repository.rest.resources.servicetemplates.propertiesdefinition.BoundaryDefsPropertiesDefinitionResource;
5759
import org.eclipse.winery.repository.rest.resources.servicetemplates.selfserviceportal.SelfServicePortalResource;
5860
import org.eclipse.winery.repository.rest.resources.servicetemplates.topologytemplates.TopologyTemplateResource;
5961
import org.eclipse.winery.repository.splitting.Splitting;
@@ -119,6 +121,11 @@ public BoundaryDefinitionsResource getBoundaryDefinitionsResource() {
119121
return new BoundaryDefinitionsResource(this, boundaryDefinitions);
120122
}
121123

124+
@Path("propertiesdefinition/")
125+
public BoundaryDefsPropertiesDefinitionResource getJson() {
126+
return new BoundaryDefsPropertiesDefinitionResource(this);
127+
}
128+
122129
@Override
123130
public String getName() {
124131
String name = this.getServiceTemplate().getName();

0 commit comments

Comments
 (0)