You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2022/07/28 05:21:46 UTC
[camel] 01/04: CAMEL-18297: Languages with namespace support should be exposed in model
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
commit f772a66f2b3379b8b348eb9fdd363a2a13b3313a
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Jul 27 18:44:50 2022 +0200
CAMEL-18297: Languages with namespace support should be exposed in model
---
.../org/apache/camel/model/language/xpath.json | 1 +
.../org/apache/camel/model/language/xquery.json | 1 +
.../org/apache/camel/model/language/xtokenize.json | 1 +
.../model/language/NamespaceAwareExpression.java | 32 +++++++++-
.../java/org/apache/camel/xml/in/ModelParser.java | 15 ++++-
.../camel/language/xtokenizer/xtokenize.json | 1 +
.../dsl/yaml/deserializers/ModelDeserializers.java | 18 ++++++
.../generated/resources/schema/camel-yaml-dsl.json | 18 ++++++
.../generated/resources/schema/camelYamlDsl.json | 18 ++++++
.../camel/dsl/yaml/XPathNamespaceTest.groovy | 71 ++++++++++++++++++++++
10 files changed, 172 insertions(+), 4 deletions(-)
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/xpath.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/xpath.json
index 26ea16c483a..8d126be2416 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/xpath.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/xpath.json
@@ -23,6 +23,7 @@
"headerName": { "kind": "attribute", "displayName": "Header Name", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Name of header to use as input, instead of the message body" },
"threadSafety": { "kind": "attribute", "displayName": "Thread Safety", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to enable thread-safety for the returned result of the xpath expression. This applies to when using NODESET as the result type, and the returned set has multiple elements. In this situation there can be thread-safety iss [...]
"preCompile": { "kind": "attribute", "displayName": "Pre Compile", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to enable pre-compiling the xpath expression during initialization phase. pre-compile is enabled by default. This can be used to turn off, for example in cases the compilation phase is desired at the starting phase, such as i [...]
+ "namespace": { "kind": "element", "displayName": "Namespace", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.PropertyDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Injects the XML Namespaces of prefix - uri mappings" },
"trim": { "kind": "attribute", "displayName": "Trim", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to trim the value to remove leading and trailing whitespaces and line breaks" },
"id": { "kind": "attribute", "displayName": "Id", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the id of this node" }
}
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/xquery.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/xquery.json
index dee4fd8e1b4..2fb4fe0ecea 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/xquery.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/xquery.json
@@ -17,6 +17,7 @@
"type": { "kind": "attribute", "displayName": "Type", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the class name of the result type (type from output) The default result type is NodeSet" },
"headerName": { "kind": "attribute", "displayName": "Header Name", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Name of header to use as input, instead of the message body" },
"configurationRef": { "kind": "attribute", "displayName": "Configuration Ref", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Reference to a saxon configuration instance in the registry to use for xquery (requires camel-saxon). This may be needed to add custom functions to a saxon configuration, so these custom functions can be used in xquery expressions." },
+ "namespace": { "kind": "element", "displayName": "Namespace", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.PropertyDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Injects the XML Namespaces of prefix - uri mappings" },
"trim": { "kind": "attribute", "displayName": "Trim", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to trim the value to remove leading and trailing whitespaces and line breaks" },
"id": { "kind": "attribute", "displayName": "Id", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the id of this node" }
}
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/xtokenize.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/xtokenize.json
index 90c9f7bc179..c0d62590912 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/xtokenize.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/xtokenize.json
@@ -16,6 +16,7 @@
"headerName": { "kind": "attribute", "displayName": "Header Name", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Name of header to tokenize instead of using the message body." },
"mode": { "kind": "attribute", "displayName": "Mode", "label": "advanced", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "i", "w", "u", "t" ], "deprecated": false, "autowired": false, "secret": false, "description": "The extraction mode. The available extraction modes are: i - injecting the contextual namespace bindings into the extracted token (default) w - wrapping the extracted token in its ancestor context u - unwrapping the extracted token to its c [...]
"group": { "kind": "attribute", "displayName": "Group", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "To group N parts together" },
+ "namespace": { "kind": "element", "displayName": "Namespace", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.PropertyDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Injects the XML Namespaces of prefix - uri mappings" },
"trim": { "kind": "attribute", "displayName": "Trim", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to trim the value to remove leading and trailing whitespaces and line breaks" },
"id": { "kind": "attribute", "displayName": "Id", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the id of this node" }
}
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/language/NamespaceAwareExpression.java b/core/camel-core-model/src/main/java/org/apache/camel/model/language/NamespaceAwareExpression.java
index 9cf236fed9d..31cc8b6220a 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/language/NamespaceAwareExpression.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/language/NamespaceAwareExpression.java
@@ -16,13 +16,18 @@
*/
package org.apache.camel.model.language;
+import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
+import org.apache.camel.model.PropertyDefinition;
import org.apache.camel.spi.NamespaceAware;
+import org.apache.camel.spi.annotations.DslProperty;
/**
* A useful base class for any expression which may be namespace or XML content aware such as {@link XPathExpression} or
@@ -31,6 +36,8 @@ import org.apache.camel.spi.NamespaceAware;
@XmlAccessorType(XmlAccessType.FIELD)
public abstract class NamespaceAwareExpression extends ExpressionDefinition implements NamespaceAware {
+ @XmlElement(name = "namespace")
+ private List<PropertyDefinition> namespace;
@XmlTransient
private Map<String, String> namespaces;
@@ -43,7 +50,7 @@ public abstract class NamespaceAwareExpression extends ExpressionDefinition impl
@Override
public Map<String, String> getNamespaces() {
- return namespaces;
+ return getNamespaceAsMap();
}
/**
@@ -56,4 +63,27 @@ public abstract class NamespaceAwareExpression extends ExpressionDefinition impl
this.namespaces = namespaces;
}
+ public List<PropertyDefinition> getNamespace() {
+ return namespace;
+ }
+
+ /**
+ * Injects the XML Namespaces of prefix -> uri mappings
+ */
+ public void setNamespace(List<PropertyDefinition> namespace) {
+ this.namespace = namespace;
+ }
+
+ public Map<String, String> getNamespaceAsMap() {
+ if (namespaces == null && namespace != null) {
+ namespaces = new HashMap<>();
+ }
+ if (namespace != null) {
+ for (PropertyDefinition def : namespace) {
+ namespaces.put(def.getKey(), def.getValue());
+ }
+ }
+ return namespaces;
+ }
+
}
diff --git a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
index 4398c2d3f46..7ca23e33695 100644
--- a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
+++ b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
@@ -2749,7 +2749,16 @@ public class ModelParser extends BaseParser {
default: return expressionDefinitionAttributeHandler().accept(def, key, val);
}
return true;
- }, noElementHandler(), expressionDefinitionValueHandler());
+ }, namespaceAwareExpressionElementHandler(), expressionDefinitionValueHandler());
+ }
+ protected <T extends NamespaceAwareExpression> ElementHandler<T> namespaceAwareExpressionElementHandler() {
+ return (def, key) -> {
+ if ("namespace".equals(key)) {
+ doAdd(doParsePropertyDefinition(), def.getNamespace(), def::setNamespace);
+ return true;
+ }
+ return false;
+ };
}
protected XPathExpression doParseXPathExpression() throws IOException, XmlPullParserException {
return doParse(new XPathExpression(), (def, key, val) -> {
@@ -2766,7 +2775,7 @@ public class ModelParser extends BaseParser {
default: return expressionDefinitionAttributeHandler().accept(def, key, val);
}
return true;
- }, noElementHandler(), expressionDefinitionValueHandler());
+ }, namespaceAwareExpressionElementHandler(), expressionDefinitionValueHandler());
}
protected XQueryExpression doParseXQueryExpression() throws IOException, XmlPullParserException {
return doParse(new XQueryExpression(), (def, key, val) -> {
@@ -2777,7 +2786,7 @@ public class ModelParser extends BaseParser {
default: return expressionDefinitionAttributeHandler().accept(def, key, val);
}
return true;
- }, noElementHandler(), expressionDefinitionValueHandler());
+ }, namespaceAwareExpressionElementHandler(), expressionDefinitionValueHandler());
}
protected CustomLoadBalancerDefinition doParseCustomLoadBalancerDefinition() throws IOException, XmlPullParserException {
return doParse(new CustomLoadBalancerDefinition(), (def, key, val) -> {
diff --git a/core/camel-xml-jaxp/src/generated/resources/org/apache/camel/language/xtokenizer/xtokenize.json b/core/camel-xml-jaxp/src/generated/resources/org/apache/camel/language/xtokenizer/xtokenize.json
index 714c2f94523..0e4568411df 100644
--- a/core/camel-xml-jaxp/src/generated/resources/org/apache/camel/language/xtokenizer/xtokenize.json
+++ b/core/camel-xml-jaxp/src/generated/resources/org/apache/camel/language/xtokenizer/xtokenize.json
@@ -19,6 +19,7 @@
"headerName": { "kind": "attribute", "displayName": "Header Name", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Name of header to tokenize instead of using the message body." },
"mode": { "kind": "attribute", "displayName": "Mode", "label": "advanced", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "i", "w", "u", "t" ], "deprecated": false, "autowired": false, "secret": false, "description": "The extraction mode. The available extraction modes are: i - injecting the contextual namespace bindings into the extracted token (default) w - wrapping the extracted token in its ancestor context u - unwrapping the extracted token to its c [...]
"group": { "kind": "attribute", "displayName": "Group", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "To group N parts together" },
+ "namespace": { "kind": "element", "displayName": "Namespace", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.PropertyDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Injects the XML Namespaces of prefix - uri mappings" },
"trim": { "kind": "attribute", "displayName": "Trim", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to trim the value to remove leading and trailing whitespaces and line breaks" },
"id": { "kind": "attribute", "displayName": "Id", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the id of this node" }
}
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
index dad10658748..8eb0bfad0e5 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
@@ -17530,6 +17530,7 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
@YamlProperty(name = "header-name", type = "string"),
@YamlProperty(name = "id", type = "string"),
@YamlProperty(name = "mode", type = "enum:i,w,u,t"),
+ @YamlProperty(name = "namespace", type = "array:org.apache.camel.model.PropertyDefinition"),
@YamlProperty(name = "trim", type = "boolean")
}
)
@@ -17572,6 +17573,11 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
target.setMode(val);
break;
}
+ case "namespace": {
+ java.util.List<org.apache.camel.model.PropertyDefinition> val = asFlatList(node, org.apache.camel.model.PropertyDefinition.class);
+ target.setNamespace(val);
+ break;
+ }
case "trim": {
String val = asText(node);
target.setTrim(val);
@@ -17606,6 +17612,7 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
@YamlProperty(name = "header-name", type = "string"),
@YamlProperty(name = "id", type = "string"),
@YamlProperty(name = "log-namespaces", type = "boolean"),
+ @YamlProperty(name = "namespace", type = "array:org.apache.camel.model.PropertyDefinition"),
@YamlProperty(name = "object-model", type = "string"),
@YamlProperty(name = "pre-compile", type = "boolean"),
@YamlProperty(name = "result-type", type = "enum:NUMBER,STRING,BOOLEAN,NODESET,NODE"),
@@ -17663,6 +17670,11 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
target.setLogNamespaces(val);
break;
}
+ case "namespace": {
+ java.util.List<org.apache.camel.model.PropertyDefinition> val = asFlatList(node, org.apache.camel.model.PropertyDefinition.class);
+ target.setNamespace(val);
+ break;
+ }
case "object-model": {
String val = asText(node);
target.setObjectModel(val);
@@ -17720,6 +17732,7 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
@YamlProperty(name = "expression", type = "string", required = true),
@YamlProperty(name = "header-name", type = "string"),
@YamlProperty(name = "id", type = "string"),
+ @YamlProperty(name = "namespace", type = "array:org.apache.camel.model.PropertyDefinition"),
@YamlProperty(name = "trim", type = "boolean"),
@YamlProperty(name = "type", type = "string")
}
@@ -17763,6 +17776,11 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
target.setId(val);
break;
}
+ case "namespace": {
+ java.util.List<org.apache.camel.model.PropertyDefinition> val = asFlatList(node, org.apache.camel.model.PropertyDefinition.class);
+ target.setNamespace(val);
+ break;
+ }
case "trim": {
String val = asText(node);
target.setTrim(val);
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camel-yaml-dsl.json b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camel-yaml-dsl.json
index edf0ecd0ca8..b51e3385be7 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camel-yaml-dsl.json
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camel-yaml-dsl.json
@@ -6405,6 +6405,12 @@
"type" : "string",
"enum" : [ "i", "w", "u", "t" ]
},
+ "namespace" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/items/definitions/org.apache.camel.model.PropertyDefinition"
+ }
+ },
"trim" : {
"type" : "boolean"
}
@@ -6435,6 +6441,12 @@
"log-namespaces" : {
"type" : "boolean"
},
+ "namespace" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/items/definitions/org.apache.camel.model.PropertyDefinition"
+ }
+ },
"object-model" : {
"type" : "string"
},
@@ -6476,6 +6488,12 @@
"id" : {
"type" : "string"
},
+ "namespace" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/items/definitions/org.apache.camel.model.PropertyDefinition"
+ }
+ },
"trim" : {
"type" : "boolean"
},
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
index 61e7b809d70..688d106ec1b 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
@@ -6306,6 +6306,12 @@
"type" : "string",
"enum" : [ "i", "w", "u", "t" ]
},
+ "namespace" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/items/definitions/org.apache.camel.model.PropertyDefinition"
+ }
+ },
"trim" : {
"type" : "boolean"
}
@@ -6336,6 +6342,12 @@
"logNamespaces" : {
"type" : "boolean"
},
+ "namespace" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/items/definitions/org.apache.camel.model.PropertyDefinition"
+ }
+ },
"objectModel" : {
"type" : "string"
},
@@ -6377,6 +6389,12 @@
"id" : {
"type" : "string"
},
+ "namespace" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/items/definitions/org.apache.camel.model.PropertyDefinition"
+ }
+ },
"trim" : {
"type" : "boolean"
},
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/XPathNamespaceTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/XPathNamespaceTest.groovy
new file mode 100644
index 00000000000..10847efc282
--- /dev/null
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/XPathNamespaceTest.groovy
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.yaml
+
+import org.apache.camel.dsl.yaml.support.YamlTestSupport
+import org.apache.camel.model.ChoiceDefinition
+import org.apache.camel.model.ToDefinition
+import org.apache.camel.model.WhenDefinition
+import org.apache.camel.model.language.XPathExpression
+
+class XPathNamespaceTest extends YamlTestSupport {
+
+ def "xpath namespace"() {
+ when:
+ loadRoutes '''
+ - from:
+ uri: "direct:start"
+ steps:
+ - choice:
+ when:
+ - expression:
+ xpath:
+ expression: "/c:number = 55"
+ namespace:
+ - key: 'c'
+ value: 'http://acme.com/cheese'
+ - key: 'w'
+ value: 'http://acme.com/wine'
+ steps:
+ - to: "mock:55"
+ otherwise:
+ steps:
+ - to: "mock:other"
+ '''
+ then:
+ context.routeDefinitions.size() == 1
+
+ with(context.routeDefinitions[0].outputs[0], ChoiceDefinition) {
+ with(whenClauses[0], WhenDefinition) {
+ var x = expression as XPathExpression
+ x.expression == '/c:number = 55'
+ x.namespace.size == 2
+ x.namespace[0].key == 'c'
+ x.namespace[0].value == 'http://acme.com/cheese'
+ x.namespace[1].key == 'w'
+ x.namespace[1].value == 'http://acme.com/wine'
+ with(outputs[0], ToDefinition) {
+ endpointUri == 'mock:55'
+ }
+ }
+ with(otherwise.outputs[0], ToDefinition) {
+ endpointUri == 'mock:other'
+ }
+ }
+ }
+
+}