Skip to content

Commit

Permalink
Allow xsd:anyType as expression output type
Browse files Browse the repository at this point in the history
Related to MID-6775.
  • Loading branch information
mederly committed Mar 5, 2021
1 parent 6a73d93 commit bd58066
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 23 deletions.
Expand Up @@ -11,6 +11,9 @@
import java.util.List;
import javax.xml.namespace.QName;

import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.QNameUtil;

import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.common.LocalizationService;
Expand Down Expand Up @@ -90,25 +93,7 @@ public <T, V extends PrismValue> List<V> evaluate(ScriptExpressionEvaluationCont
return evalPrismValues;
}

QName xsdReturnType = context.getOutputDefinition().getTypeName();

Class<T> javaReturnType = XsdTypeMapper.toJavaType(xsdReturnType);
if (javaReturnType == null) {
javaReturnType = getPrismContext().getSchemaRegistry().getCompileTimeClass(xsdReturnType);
}

if (javaReturnType == null && (context.getOutputDefinition() instanceof PrismContainerDefinition<?>)) {
// This is the case when we need a container, but we do not have compile-time class for that
// E.g. this may be container in object extension (MID-5080)
javaReturnType = (Class<T>) PrismContainerValue.class;
}

if (javaReturnType == null) {
// TODO quick and dirty hack - because this could be because of enums defined in schema extension (MID-2399)
// ...and enums (xsd:simpleType) are not parsed into ComplexTypeDefinitions
javaReturnType = (Class<T>) String.class;
}
LOGGER.trace("expected return type: XSD={}, Java={}", xsdReturnType, javaReturnType);
Class<T> javaReturnType = determineJavaReturnType(context);

List<V> values = new ArrayList<>();

Expand All @@ -132,6 +117,36 @@ public <T, V extends PrismValue> List<V> evaluate(ScriptExpressionEvaluationCont
return values;
}

@NotNull
private <T> Class<T> determineJavaReturnType(ScriptExpressionEvaluationContext context) {
QName xsdReturnType = context.getOutputDefinition().getTypeName();

// Ugly hack. Indented to allow xsd:anyType return type, see MID-6775.
if (QNameUtil.match(xsdReturnType, DOMUtil.XSD_ANYTYPE)) {
//noinspection unchecked
return (Class<T>) Object.class;
}

Class<T> javaReturnType = XsdTypeMapper.toJavaType(xsdReturnType);
if (javaReturnType == null) {
javaReturnType = getPrismContext().getSchemaRegistry().getCompileTimeClass(xsdReturnType);
}

if (javaReturnType == null && (context.getOutputDefinition() instanceof PrismContainerDefinition<?>)) {
// This is the case when we need a container, but we do not have compile-time class for that
// E.g. this may be container in object extension (MID-5080)
javaReturnType = (Class<T>) PrismContainerValue.class;
}

if (javaReturnType == null) {
// TODO quick and dirty hack - because this could be because of enums defined in schema extension (MID-2399)
// ...and enums (xsd:simpleType) are not parsed into ComplexTypeDefinitions
javaReturnType = (Class<T>) String.class;
}
LOGGER.trace("expected return type: XSD={}, Java={}", xsdReturnType, javaReturnType);
return javaReturnType;
}

private C getCompiledScript(String codeString, ScriptExpressionEvaluationContext context) throws ExpressionEvaluationException, SecurityViolationException {
C compiledScript = scriptCache.getCode(context.getExpressionProfile(), codeString);
if (compiledScript != null) {
Expand Down
Expand Up @@ -307,8 +307,25 @@ public void testCustomFunctionWrongParameter() throws Exception {
}
}

@Test
public void testCustomFunctionUntyped() throws Exception {
PrismContainer<Containerable> customPc = createCustomContainer();
PrismContainerValue<Containerable> customPcv = createCustomValue();

VariablesMap variables = VariablesMap.create(prismContext,
"var1", customPc.clone().getValues(), customPc.getDefinition(),
"var2", customPcv.clone(), customPcv.getDefinition());

assertExecuteScriptExpressionString(variables, "s-1");
}

@NotNull
private PrismContainerValue<Containerable> createCustomValue() throws SchemaException {
return createCustomContainer().getValue();
}

@NotNull
private PrismContainer<Containerable> createCustomContainer() throws SchemaException {
PrismContainerDefinition<Containerable> customDef =
prismContext.getSchemaRegistry().findContainerDefinitionByElementName(CUSTOM);
PrismContainer<Containerable> customPc = customDef.instantiate();
Expand All @@ -320,7 +337,7 @@ private PrismContainerValue<Containerable> createCustomValue() throws SchemaExce

customPcv.add(stringPp);
customPcv.add(intPp);
return customPcv;
return customPc;
}

private void assertExecuteScriptExpressionString(
Expand All @@ -335,7 +352,8 @@ private String executeScriptExpressionString(VariablesMap variables)
throws SecurityViolationException, ExpressionEvaluationException, SchemaException,
ObjectNotFoundException, CommunicationException, ConfigurationException, IOException {
// GIVEN
OperationResult result = createOperationResult();
Task task = getTestTask();
OperationResult result = task.getResult();
String shortTestName = getTestNameShort();

ScriptExpressionEvaluatorType scriptType = parseScriptType("expression-" + shortTestName + ".xml");
Expand Down Expand Up @@ -374,7 +392,8 @@ private String executeScriptExpressionString(VariablesMap variables)

@SuppressWarnings("SameParameterValue")
private <T> List<PrismPropertyValue<T>> evaluate(ScriptExpression scriptExpression, VariablesMap variables, boolean useNew,
String contextDescription, Task task, OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException {
String contextDescription, Task task, OperationResult result) throws ExpressionEvaluationException,
ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException {
if (task == null) {
task = taskManager.createTaskInstance();
}
Expand All @@ -394,5 +413,4 @@ private <T> List<PrismPropertyValue<T>> evaluate(ScriptExpression scriptExpressi
ModelExpressionThreadLocalHolder.popExpressionEnvironment();
}
}

}
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2010-2017 Evolveum and contributors
~
~ This work is dual-licensed under the Apache License 2.0
~ and European Union Public License. See LICENSE file for details.
-->

<script xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3">
<code>
import com.evolveum.midpoint.prism.path.ItemPath

pcv = functionLibrary.execute("untyped", [container: var1, containerValue: var2])
pcv?.findProperty(ItemPath.create('stringValue'))?.getRealValue() + '-' +
pcv?.findProperty(ItemPath.create('intValue'))?.getRealValue()
</code>
</script>
24 changes: 24 additions & 0 deletions model/model-impl/src/test/resources/expr/function-library.xml
Expand Up @@ -35,4 +35,28 @@
</code>
</script>
</function>

<function>
<name>untyped</name>
<parameter>
<name>container</name>
<!-- Although it is possible to provide a PrismContainer here, it is more advisable
to send a list of PrismContainerValues. (Just BTW, we can use a specific type then!) -->
<type>xsd:anyType</type>
</parameter>
<parameter>
<name>containerValue</name>
<type>xsd:anyType</type> <!-- Expects PrismContainerValue. Let us assume it is of any type. -->
</parameter>
<returnType>xsd:anyType</returnType> <!-- Returns PrismContainerValue -->
<script>
<code>
import com.evolveum.prism.xml.ns._public.types_3.ItemType

log.info('container = {} ({})', container, container?.class)
log.info('containerValue = {} ({})', containerValue, containerValue?.class)
container?.get(0)
</code>
</script>
</function>
</functionLibrary>

0 comments on commit bd58066

Please sign in to comment.