Skip to content

Commit

Permalink
Add initial implementation
Browse files Browse the repository at this point in the history
Signed-off-by: Vladimir Yussupov <v.yussupov@gmail.com>
  • Loading branch information
v-yussupov committed Jul 19, 2018
1 parent 8f93851 commit f2fbee3
Show file tree
Hide file tree
Showing 12 changed files with 475 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,31 @@
package org.eclipse.winery.model.tosca;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.eclipse.winery.model.tosca.constants.Namespaces;

import io.github.adr.embedded.ADR;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

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

@Nullable
public Object getAny() {
return any;
if (this.getKVProperties() == null) {
return any;
} else {
return null;
}
}

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

/**
* This is a special method for Winery. Winery allows to define a property by specifying name/value values.
* Instead of parsing the XML contained in TNodeType, this method is a convenience method to access this
* information assumes the properties are key/value pairs (see WinerysPropertiesDefinition), all other cases are
* return null.
* <p>
* Returns a map of key/values of this template based on the information of WinerysPropertiesDefinition. In case
* no value is set, the empty string is used. The map is implemented as {@link LinkedHashMap} to ensure that the
* order of the elements is the same as in the XML. We return the type {@link LinkedHashMap}, because there is
* no appropriate Java interface for "sorted" Maps
* <p>
* In case the element is not of the form k/v, null is returned
* <p>
* This method assumes that the any field is always populated.
*
* @return null if not k/v, a map of k/v properties otherwise
*/
@ADR(12)
public LinkedHashMap<String, String> getKVProperties() {

This comment has been minimized.

Copy link
@lharzenetter

lharzenetter Jul 19, 2018

Member

Instead of copy paste this, create an ?abstract class, interface? which handles this stuff and refactor NodeTypes, etc.
Or Utility class...

// we use the internal variable "any", because getAny() returns null, if we have KVProperties
if (any == null || !(any instanceof Element)) {
return null;
}

Element el = (Element) any;
if (el == null) {
return null;
}

// we have no type information in this place
// we could inject a repository, but if Winery is used with multiple repositories, this could cause race conditions
// therefore, we guess at the instance of the properties definition (i.e., here) if it is key/value or not.

boolean isKv = true;

LinkedHashMap<String, String> properties = new LinkedHashMap<>();

NodeList childNodes = el.getChildNodes();

if (childNodes.getLength() == 0) {
// somehow invalid XML - do not treat it as k/v
return null;
}

for (int i = 0; i < childNodes.getLength(); i++) {
Node item = childNodes.item(i);
if (item instanceof Element) {
String key = item.getLocalName();
String value;

Element kvElement = (Element) item;
NodeList kvElementChildNodes = kvElement.getChildNodes();
if (kvElementChildNodes.getLength() == 0) {
value = "";
} else if (kvElementChildNodes.getLength() > 1) {
// This is a wrong guess if comments are used, but this is prototype
isKv = false;
break;
} else {
// one child - just get the text.
value = item.getTextContent();
}
properties.put(key, value);
} else if (item instanceof Text || item instanceof Comment) {
// these kinds of nodes are OK
}
}

if (isKv) {
return properties;
} else {
return null;
}
}

@ADR(12)
public void setKVProperties(Map<String, String> properties) {
Objects.requireNonNull(properties);
Element el = (Element) any;

if (el == null) {
// special case if JSON is parsed without updating an existing element
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db;
try {
db = dbf.newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new IllegalStateException("Could not instantiate document builder", e);
}
Document doc = db.newDocument();

// We cannot access the wrapper definitions, because we don't have access to the type
// Element root = doc.createElementNS(wpd.getNamespace(), wpd.getElementName());
// Therefore, we create a dummy wrapper element:

Element root = doc.createElementNS(Namespaces.EXAMPLE_NAMESPACE_URI, "Properties");
doc.appendChild(root);

// No wpd - so this is not possible:
// we produce the serialization in the same order the XSD would be generated (because of the usage of xsd:sequence)
// for (PropertyDefinitionKV prop : wpd.getPropertyDefinitionKVList()) {

for (String key : properties.keySet()) {
// wpd.getNamespace()
Element element = doc.createElementNS(Namespaces.EXAMPLE_NAMESPACE_URI, key);
root.appendChild(element);
String value = properties.get(key);
if (value != null) {
Text text = doc.createTextNode(value);
element.appendChild(text);
}
}

this.setAny(doc.getDocumentElement());
} else {
// straight-forward copy over to existing property structure
NodeList childNodes = el.getChildNodes();

for (int i = 0; i < childNodes.getLength(); i++) {
Node item = childNodes.item(i);
if (item instanceof Element) {
final Element element = (Element) item;
final String key = element.getLocalName();
final String value = properties.get(key);
if (value != null) {
element.setTextContent(value);
}
} else if (item instanceof Text || item instanceof Comment) {
// these kinds of nodes are OK
}
}
}
}

public TBoundaryDefinitions.Properties.@Nullable PropertyMappings getPropertyMappings() {
return propertyMappings;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,34 @@
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
import javax.xml.namespace.QName;

import org.eclipse.winery.model.tosca.constants.Namespaces;
import org.eclipse.winery.model.tosca.kvproperties.WinerysPropertiesDefinition;

import com.fasterxml.jackson.annotation.JsonIgnore;
import org.eclipse.jdt.annotation.Nullable;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "tServiceTemplate", propOrder = {
"propertiesDefinition",
"tags",
"boundaryDefinitions",
"topologyTemplate",
"plans"
})
public class TServiceTemplate extends HasId implements HasName, HasTargetNamespace {
public static final String NS_SUFFIX_PROPERTIESDEFINITION_WINERY = "propertiesdefinition/winery";

@XmlElements( {
@XmlElement(name = "PropertiesDefinition", namespace = Namespaces.TOSCA_WINERY_EXTENSIONS_NAMESPACE, type = TServiceTemplate.PropertiesDefinition.class),
@XmlElement(name = "PropertiesDefinition", namespace = Namespaces.TOSCA_WINERY_EXTENSIONS_NAMESPACE, type = WinerysPropertiesDefinition.class)
})
protected Object propertiesDefinition;

@XmlElement(name = "Tags")
protected TTags tags;
Expand Down Expand Up @@ -158,6 +172,74 @@ public void setSubstitutableNodeType(QName value) {
this.substitutableNodeType = value;
}

public Object getPropertiesDefinition() {
return propertiesDefinition;
}

public void setPropertiesDefinition(Object value) {
this.propertiesDefinition = value;
}

/**
* <p>Java class for anonymous complex type.
* <p>
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
* <pre>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;attribute name="element" type="{http://www.w3.org/2001/XMLSchema}QName" />
* &lt;attribute name="type" type="{http://www.w3.org/2001/XMLSchema}QName" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "")
public static class PropertiesDefinition extends TEntityType.PropertiesDefinition {
}

/**
* This is a special method for Winery. Winery allows to define a property definition by specifying name/type
* values. Instead of parsing the extensible elements returned TDefinitions, this method is a convenience method to
* access this information
*
* @return a WinerysPropertiesDefinition object, which includes a map of name/type-pairs denoting the associated
* property definitions. A default element name and namespace is added if it is not defined in the underlying XML.
* null if no Winery specific KV properties are defined for the given entity type
*/
@XmlTransient
@JsonIgnore
public WinerysPropertiesDefinition getWinerysPropertiesDefinition() {
// similar implementation as org.eclipse.winery.repository.resources.entitytypes.properties.PropertiesDefinitionResource.getListFromEntityType(TEntityType)
WinerysPropertiesDefinition res = null;
if (this.getPropertiesDefinition() instanceof WinerysPropertiesDefinition) {
res = (WinerysPropertiesDefinition) this.getPropertiesDefinition();
}

if (res != null) {
// we put defaults if elementname and namespace have not been set

if (res.getElementName() == null) {
res.setElementName("Properties");
}

if (res.getNamespace() == null) {
// we use the targetnamespace of the original element
String ns = this.getTargetNamespace();
if (!ns.endsWith("/")) {
ns += "/";
}
ns += NS_SUFFIX_PROPERTIESDEFINITION_WINERY;
res.setNamespace(ns);
}
}

return res;
}

public static class Builder extends HasId.Builder<Builder> {
private final TTopologyTemplate topologyTemplate;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,23 @@ public PropertiesDefinitionResourceApiData(
this.selectedValue = PropertiesDefinitionEnum.None;
}
}

public PropertiesDefinitionResourceApiData(Object propertiesDefinition) {

This comment has been minimized.

Copy link
@lharzenetter

lharzenetter Jul 19, 2018

Member

Not quite sure if we need this

if (propertiesDefinition instanceof TEntityType.PropertiesDefinition) {
this.propertiesDefinition = (TEntityType.PropertiesDefinition) propertiesDefinition;
}
if (propertiesDefinition instanceof WinerysPropertiesDefinition) {
this.winerysPropertiesDefinition = (WinerysPropertiesDefinition) propertiesDefinition;
}

if ((winerysPropertiesDefinition != null) && (winerysPropertiesDefinition.getIsDerivedFromXSD() == null)) {
this.selectedValue = PropertiesDefinitionEnum.Custom;
} else if ((this.propertiesDefinition != null) && (this.propertiesDefinition.getElement() != null)) {
this.selectedValue = PropertiesDefinitionEnum.Element;
} else if ((this.propertiesDefinition != null) && (this.propertiesDefinition.getType() != null)) {
this.selectedValue = PropertiesDefinitionEnum.Type;
} else {
this.selectedValue = PropertiesDefinitionEnum.None;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,10 @@
import org.eclipse.winery.repository.rest.resources._support.IHasName;
import org.eclipse.winery.repository.rest.resources._support.dataadapter.injectionadapter.InjectorReplaceData;
import org.eclipse.winery.repository.rest.resources._support.dataadapter.injectionadapter.InjectorReplaceOptions;
import org.eclipse.winery.repository.rest.resources.apiData.PropertiesDefinitionResourceApiData;
import org.eclipse.winery.repository.rest.resources.servicetemplates.boundarydefinitions.BoundaryDefinitionsResource;
import org.eclipse.winery.repository.rest.resources.servicetemplates.plans.PlansResource;
import org.eclipse.winery.repository.rest.resources.servicetemplates.propertiesdefinition.BoundaryDefsPropertiesDefinitionResource;
import org.eclipse.winery.repository.rest.resources.servicetemplates.selfserviceportal.SelfServicePortalResource;
import org.eclipse.winery.repository.rest.resources.servicetemplates.topologytemplates.TopologyTemplateResource;
import org.eclipse.winery.repository.splitting.Splitting;
Expand Down Expand Up @@ -119,6 +121,11 @@ public BoundaryDefinitionsResource getBoundaryDefinitionsResource() {
return new BoundaryDefinitionsResource(this, boundaryDefinitions);
}

@Path("propertiesdefinition/")
public BoundaryDefsPropertiesDefinitionResource getJson() {
return new BoundaryDefsPropertiesDefinitionResource(this);
}

@Override
public String getName() {
String name = this.getServiceTemplate().getName();
Expand Down
Loading

0 comments on commit f2fbee3

Please sign in to comment.