diff --git a/activiti-engine/src/main/java/org/activiti/engine/impl/el/JsonNodeELResolver.java b/activiti-engine/src/main/java/org/activiti/engine/impl/el/JsonNodeELResolver.java index 4f8a0194cae5431280dc52c504327c6310f9e09b..096a285edd24e5bed6573f7190ed1125971db0df 100644 --- a/activiti-engine/src/main/java/org/activiti/engine/impl/el/JsonNodeELResolver.java +++ b/activiti-engine/src/main/java/org/activiti/engine/impl/el/JsonNodeELResolver.java @@ -1,10 +1,10 @@ -/* +/* * Licensed 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. @@ -31,310 +31,342 @@ import com.fasterxml.jackson.databind.node.ObjectNode; /** * Defines property resolution behavior on JsonNodes. - * + * * @see CompositeELResolver * @see ELResolver */ public class JsonNodeELResolver extends ELResolver { - private final boolean readOnly; - - /** - * Creates a new read/write BeanELResolver. - */ - public JsonNodeELResolver() { - this(false); - } + private final boolean readOnly; - /** - * Creates a new BeanELResolver whose read-only status is determined by the given parameter. - */ - public JsonNodeELResolver(boolean readOnly) { - this.readOnly = readOnly; - } + /** + * Creates a new read/write BeanELResolver. + */ + public JsonNodeELResolver() { + this(false); + } - /** - * If the base object is not null, returns the most general type that this resolver accepts for - * the property argument. Otherwise, returns null. Assuming the base is not null, this method - * will always return Object.class. This is because any object is accepted as a key and is - * coerced into a string. - * - * @param context - * The context of this evaluation. - * @param base - * The bean to analyze. - * @return null if base is null; otherwise Object.class. - */ - @Override - public Class getCommonPropertyType(ELContext context, Object base) { - return isResolvable(base) ? Object.class : null; - } + /** + * Creates a new BeanELResolver whose read-only status is determined by the + * given parameter. + */ + public JsonNodeELResolver(boolean readOnly) { + this.readOnly = readOnly; + } - /** - * If the base object is not null, returns an Iterator containing the set of JavaBeans - * properties available on the given object. Otherwise, returns null. The Iterator returned must - * contain zero or more instances of java.beans.FeatureDescriptor. Each info object contains - * information about a property in the bean, as obtained by calling the - * BeanInfo.getPropertyDescriptors method. The FeatureDescriptor is initialized using the same - * fields as are present in the PropertyDescriptor, with the additional required named - * attributes "type" and "resolvableAtDesignTime" set as follows: - * - * - * @param context - * The context of this evaluation. - * @param base - * The bean to analyze. - * @return An Iterator containing zero or more FeatureDescriptor objects, each representing a - * property on this bean, or null if the base object is null. - */ - @Override - public Iterator getFeatureDescriptors(ELContext context, Object base) { - if (isResolvable(base)) { - JsonNode node = (JsonNode) base; - final Iterator keys = node.fieldNames(); - return new Iterator() { - public boolean hasNext() { - return keys.hasNext(); - } - public FeatureDescriptor next() { - Object key = keys.next(); - FeatureDescriptor feature = new FeatureDescriptor(); - feature.setDisplayName(key == null ? "null" : key.toString()); - feature.setName(feature.getDisplayName()); - feature.setShortDescription(""); - feature.setExpert(true); - feature.setHidden(false); - feature.setPreferred(true); - feature.setValue(TYPE, key == null ? "null" : key.getClass()); - feature.setValue(RESOLVABLE_AT_DESIGN_TIME, true); - return feature; - - } - public void remove() { - throw new UnsupportedOperationException("cannot remove"); - } - }; - } - return null; - } + /** + * If the base object is not null, returns the most general type that this + * resolver accepts for the property argument. Otherwise, returns null. + * Assuming the base is not null, this method will always return + * Object.class. This is because any object is accepted as a key and is + * coerced into a string. + * + * @param context + * The context of this evaluation. + * @param base + * The bean to analyze. + * @return null if base is null; otherwise Object.class. + */ + @Override + public Class getCommonPropertyType(ELContext context, Object base) { + return isResolvable(base) ? Object.class : null; + } - /** - * If the base object is a map, returns the most general acceptable type for a value in this - * map. If the base is a Map, the propertyResolved property of the ELContext object must be set - * to true by this resolver, before returning. If this property is not true after this method is - * called, the caller should ignore the return value. Assuming the base is a Map, this method - * will always return Object.class. This is because Maps accept any object as the value for a - * given key. - * - * @param context - * The context of this evaluation. - * @param base - * The map to analyze. Only bases of type Map are handled by this resolver. - * @param property - * The key to return the acceptable type for. Ignored by this resolver. - * @return If the propertyResolved property of ELContext was set to true, then the most general - * acceptable type; otherwise undefined. - * @throws NullPointerException - * if context is null - * @throws ELException - * if an exception was thrown while performing the property or variable resolution. - * The thrown exception must be included as the cause property of this exception, if - * available. - */ - @Override - public Class getType(ELContext context, Object base, Object property) { - if (context == null) { - throw new NullPointerException("context is null"); - } - Class result = null; - if (isResolvable(base)) { - result = Object.class; - context.setPropertyResolved(true); - } - return result; - } + /** + * If the base object is not null, returns an Iterator containing the set of + * JavaBeans properties available on the given object. Otherwise, returns + * null. The Iterator returned must contain zero or more instances of + * java.beans.FeatureDescriptor. Each info object contains information about + * a property in the bean, as obtained by calling the + * BeanInfo.getPropertyDescriptors method. The FeatureDescriptor is + * initialized using the same fields as are present in the + * PropertyDescriptor, with the additional required named attributes "type" + * and "resolvableAtDesignTime" set as follows: + *
    + *
  • {@link ELResolver#TYPE} - The runtime type of the property, from + * PropertyDescriptor.getPropertyType().
  • + *
  • {@link ELResolver#RESOLVABLE_AT_DESIGN_TIME} - true.
  • + *
+ * + * @param context + * The context of this evaluation. + * @param base + * The bean to analyze. + * @return An Iterator containing zero or more FeatureDescriptor objects, + * each representing a property on this bean, or null if the base + * object is null. + */ + @Override + public Iterator getFeatureDescriptors(ELContext context, Object base) { + if (isResolvable(base)) { + JsonNode node = (JsonNode) base; + final Iterator keys = node.fieldNames(); + return new Iterator() { + @Override + public boolean hasNext() { + return keys.hasNext(); + } - /** - * If the base object is a map, returns the value associated with the given key, as specified by - * the property argument. If the key was not found, null is returned. If the base is a Map, the - * propertyResolved property of the ELContext object must be set to true by this resolver, - * before returning. If this property is not true after this method is called, the caller should - * ignore the return value. Just as in java.util.Map.get(Object), just because null is returned - * doesn't mean there is no mapping for the key; it's also possible that the Map explicitly maps - * the key to null. - * - * @param context - * The context of this evaluation. - * @param base - * The map to analyze. Only bases of type Map are handled by this resolver. - * @param property - * The key to return the acceptable type for. Ignored by this resolver. - * @return If the propertyResolved property of ELContext was set to true, then the value - * associated with the given key or null if the key was not found. Otherwise, undefined. - * @throws ClassCastException - * if the key is of an inappropriate type for this map (optionally thrown by the - * underlying Map). - * @throws NullPointerException - * if context is null, or if the key is null and this map does not permit null keys - * (the latter is optionally thrown by the underlying Map). - * @throws ELException - * if an exception was thrown while performing the property or variable resolution. - * The thrown exception must be included as the cause property of this exception, if - * available. - */ - @Override - public Object getValue(ELContext context, Object base, Object property) { - if (context == null) { - throw new NullPointerException("context is null"); - } - Object result = null; - if (isResolvable(base)) { - JsonNode resultNode = ((JsonNode) base).get(property.toString()); - if (resultNode != null && resultNode.isValueNode()) { - if (resultNode.isBoolean()) { - result = resultNode.asBoolean(); - } else if (resultNode.isLong()) { - result = resultNode.asLong(); - } else if (resultNode.isBigDecimal() || resultNode.isDouble()) { - result = resultNode.asDouble(); - } else if (resultNode.isTextual()) { - result = resultNode.asText(); - } else { - result = resultNode.toString(); - } - - } else { - if (resultNode.isArray()) { - result = Context.getProcessEngineConfiguration().getObjectMapper().convertValue(resultNode, List.class); - } else { - result = resultNode; - } - } - context.setPropertyResolved(true); - } - return result; - } + @Override + public FeatureDescriptor next() { + Object key = keys.next(); + FeatureDescriptor feature = new FeatureDescriptor(); + feature.setDisplayName(key == null ? "null" : key.toString()); + feature.setName(feature.getDisplayName()); + feature.setShortDescription(""); + feature.setExpert(true); + feature.setHidden(false); + feature.setPreferred(true); + feature.setValue(TYPE, key == null ? "null" : key.getClass()); + feature.setValue(RESOLVABLE_AT_DESIGN_TIME, true); + return feature; - /** - * If the base object is a map, returns whether a call to - * {@link #setValue(ELContext, Object, Object, Object)} will always fail. If the base is a Map, - * the propertyResolved property of the ELContext object must be set to true by this resolver, - * before returning. If this property is not true after this method is called, the caller should - * ignore the return value. If this resolver was constructed in read-only mode, this method will - * always return true. If a Map was created using java.util.Collections.unmodifiableMap(Map), - * this method must return true. Unfortunately, there is no Collections API method to detect - * this. However, an implementation can create a prototype unmodifiable Map and query its - * runtime type to see if it matches the runtime type of the base object as a workaround. - * - * @param context - * The context of this evaluation. - * @param base - * The map to analyze. Only bases of type Map are handled by this resolver. - * @param property - * The key to return the acceptable type for. Ignored by this resolver. - * @return If the propertyResolved property of ELContext was set to true, then true if calling - * the setValue method will always fail or false if it is possible that such a call may - * succeed; otherwise undefined. - * @throws NullPointerException - * if context is null. - * @throws ELException - * if an exception was thrown while performing the property or variable resolution. - * The thrown exception must be included as the cause property of this exception, if - * available. - */ - @Override - public boolean isReadOnly(ELContext context, Object base, Object property) { - if (context == null) { - throw new NullPointerException("context is null"); - } - if (isResolvable(base)) { - context.setPropertyResolved(true); - } - return readOnly; - } + } - /** - * If the base object is a map, attempts to set the value associated with the given key, as - * specified by the property argument. If the base is a Map, the propertyResolved property of - * the ELContext object must be set to true by this resolver, before returning. If this property - * is not true after this method is called, the caller can safely assume no value was set. If - * this resolver was constructed in read-only mode, this method will always throw - * PropertyNotWritableException. If a Map was created using - * java.util.Collections.unmodifiableMap(Map), this method must throw - * PropertyNotWritableException. Unfortunately, there is no Collections API method to detect - * this. However, an implementation can create a prototype unmodifiable Map and query its - * runtime type to see if it matches the runtime type of the base object as a workaround. - * - * @param context - * The context of this evaluation. - * @param base - * The map to analyze. Only bases of type Map are handled by this resolver. - * @param property - * The key to return the acceptable type for. Ignored by this resolver. - * @param value - * The value to be associated with the specified key. - * @throws ClassCastException - * if the class of the specified key or value prevents it from being stored in this - * map. - * @throws NullPointerException - * if context is null, or if this map does not permit null keys or values, and the - * specified key or value is null. - * @throws IllegalArgumentException - * if some aspect of this key or value prevents it from being stored in this map. - * @throws PropertyNotWritableException - * if this resolver was constructed in read-only mode, or if the put operation is - * not supported by the underlying map. - * @throws ELException - * if an exception was thrown while performing the property or variable resolution. - * The thrown exception must be included as the cause property of this exception, if - * available. - */ - @Override - public void setValue(ELContext context, Object base, Object property, Object value) { - if (context == null) { - throw new NullPointerException("context is null"); - } - if (base instanceof ObjectNode) { - if (readOnly) { - throw new PropertyNotWritableException("resolver is read-only"); - } - ObjectNode node = (ObjectNode) base; - if (value instanceof BigDecimal) { - node.put(property.toString(), (BigDecimal) value); - - } else if (value instanceof Boolean) { - node.put(property.toString(), (Boolean) value); - - } else if (value instanceof Long) { - node.put(property.toString(), (Long) value); - - } else if (value instanceof Double) { - node.put(property.toString(), (Double) value); - - } else if (value != null) { - node.put(property.toString(), value.toString()); - - } else { - node.putNull(property.toString()); - } - context.setPropertyResolved(true); - } - } - - /** - * Test whether the given base should be resolved by this ELResolver. - * - * @param base - * The bean to analyze. - * @param property - * The name of the property to analyze. Will be coerced to a String. - * @return base != null - */ - private final boolean isResolvable(Object base) { - return base instanceof JsonNode; - } + @Override + public void remove() { + throw new UnsupportedOperationException("cannot remove"); + } + }; + } + return null; + } + + /** + * If the base object is a map, returns the most general acceptable type for + * a value in this map. If the base is a Map, the propertyResolved property + * of the ELContext object must be set to true by this resolver, before + * returning. If this property is not true after this method is called, the + * caller should ignore the return value. Assuming the base is a Map, this + * method will always return Object.class. This is because Maps accept any + * object as the value for a given key. + * + * @param context + * The context of this evaluation. + * @param base + * The map to analyze. Only bases of type Map are handled by this + * resolver. + * @param property + * The key to return the acceptable type for. Ignored by this + * resolver. + * @return If the propertyResolved property of ELContext was set to true, + * then the most general acceptable type; otherwise undefined. + * @throws NullPointerException + * if context is null + * @throws ELException + * if an exception was thrown while performing the property or + * variable resolution. The thrown exception must be included as + * the cause property of this exception, if available. + */ + @Override + public Class getType(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException("context is null"); + } + Class result = null; + if (isResolvable(base)) { + result = Object.class; + context.setPropertyResolved(true); + } + return result; + } + + /** + * If the base object is a map, returns the value associated with the given + * key, as specified by the property argument. If the key was not found, + * null is returned. If the base is a Map, the propertyResolved property of + * the ELContext object must be set to true by this resolver, before + * returning. If this property is not true after this method is called, the + * caller should ignore the return value. Just as in + * java.util.Map.get(Object), just because null is returned doesn't mean + * there is no mapping for the key; it's also possible that the Map + * explicitly maps the key to null. + * + * @param context + * The context of this evaluation. + * @param base + * The map to analyze. Only bases of type Map are handled by this + * resolver. + * @param property + * The key to return the acceptable type for. Ignored by this + * resolver. + * @return If the propertyResolved property of ELContext was set to true, + * then the value associated with the given key or null if the key + * was not found. Otherwise, undefined. + * @throws ClassCastException + * if the key is of an inappropriate type for this map + * (optionally thrown by the underlying Map). + * @throws NullPointerException + * if context is null, or if the key is null and this map does + * not permit null keys (the latter is optionally thrown by the + * underlying Map). + * @throws ELException + * if an exception was thrown while performing the property or + * variable resolution. The thrown exception must be included as + * the cause property of this exception, if available. + */ + @Override + public Object getValue(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException("context is null"); + } + Object result = null; + if (isResolvable(base)) { + JsonNode resultNode = ((JsonNode) base).get(property.toString()); + if (resultNode != null && resultNode.isValueNode()) { + if (resultNode.isBoolean()) { + result = resultNode.asBoolean(); + } else if (resultNode.isLong()) { + result = resultNode.asLong(); + } else if (resultNode.isBigDecimal() || resultNode.isDouble()) { + result = resultNode.asDouble(); + } else if (resultNode.isTextual()) { + result = resultNode.asText(); + } else { + result = resultNode.toString(); + } + + } else { + if (resultNode.isArray()) { + result = Context.getProcessEngineConfiguration().getObjectMapper().convertValue(resultNode, + List.class); + } else { + result = resultNode; + } + } + context.setPropertyResolved(true); + } + return result; + } + + /** + * If the base object is a map, returns whether a call to + * {@link #setValue(ELContext, Object, Object, Object)} will always fail. If + * the base is a Map, the propertyResolved property of the ELContext object + * must be set to true by this resolver, before returning. If this property + * is not true after this method is called, the caller should ignore the + * return value. If this resolver was constructed in read-only mode, this + * method will always return true. If a Map was created using + * java.util.Collections.unmodifiableMap(Map), this method must return true. + * Unfortunately, there is no Collections API method to detect this. + * However, an implementation can create a prototype unmodifiable Map and + * query its runtime type to see if it matches the runtime type of the base + * object as a workaround. + * + * @param context + * The context of this evaluation. + * @param base + * The map to analyze. Only bases of type Map are handled by this + * resolver. + * @param property + * The key to return the acceptable type for. Ignored by this + * resolver. + * @return If the propertyResolved property of ELContext was set to true, + * then true if calling the setValue method will always fail or + * false if it is possible that such a call may succeed; otherwise + * undefined. + * @throws NullPointerException + * if context is null. + * @throws ELException + * if an exception was thrown while performing the property or + * variable resolution. The thrown exception must be included as + * the cause property of this exception, if available. + */ + @Override + public boolean isReadOnly(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException("context is null"); + } + if (isResolvable(base)) { + context.setPropertyResolved(true); + } + return readOnly; + } + + /** + * If the base object is a map, attempts to set the value associated with + * the given key, as specified by the property argument. If the base is a + * Map, the propertyResolved property of the ELContext object must be set to + * true by this resolver, before returning. If this property is not true + * after this method is called, the caller can safely assume no value was + * set. If this resolver was constructed in read-only mode, this method will + * always throw PropertyNotWritableException. If a Map was created using + * java.util.Collections.unmodifiableMap(Map), this method must throw + * PropertyNotWritableException. Unfortunately, there is no Collections API + * method to detect this. However, an implementation can create a prototype + * unmodifiable Map and query its runtime type to see if it matches the + * runtime type of the base object as a workaround. + * + * @param context + * The context of this evaluation. + * @param base + * The map to analyze. Only bases of type Map are handled by this + * resolver. + * @param property + * The key to return the acceptable type for. Ignored by this + * resolver. + * @param value + * The value to be associated with the specified key. + * @throws ClassCastException + * if the class of the specified key or value prevents it from + * being stored in this map. + * @throws NullPointerException + * if context is null, or if this map does not permit null keys + * or values, and the specified key or value is null. + * @throws IllegalArgumentException + * if some aspect of this key or value prevents it from being + * stored in this map. + * @throws PropertyNotWritableException + * if this resolver was constructed in read-only mode, or if the + * put operation is not supported by the underlying map. + * @throws ELException + * if an exception was thrown while performing the property or + * variable resolution. The thrown exception must be included as + * the cause property of this exception, if available. + */ + @Override + public void setValue(ELContext context, Object base, Object property, Object value) { + if (context == null) { + throw new NullPointerException("context is null"); + } + if (base instanceof ObjectNode) { + if (readOnly) { + throw new PropertyNotWritableException("resolver is read-only"); + } + ObjectNode node = (ObjectNode) base; + if (value instanceof BigDecimal) { + node.put(property.toString(), (BigDecimal) value); + + } else if (value instanceof Boolean) { + node.put(property.toString(), (Boolean) value); + + } else if (value instanceof Long) { + node.put(property.toString(), (Long) value); + + } else if (value instanceof Double) { + node.put(property.toString(), (Double) value); + + } else if (value != null) { + node.put(property.toString(), value.toString()); + + } else { + node.putNull(property.toString()); + } + context.setPropertyResolved(true); + } + } + + /** + * Test whether the given base should be resolved by this ELResolver. + * + * @param base + * The bean to analyze. + * @param property + * The name of the property to analyze. Will be coerced to a + * String. + * @return base != null + */ + private final boolean isResolvable(Object base) { + return base instanceof JsonNode; + } } diff --git a/activiti-engine/src/main/java/org/activiti/engine/impl/el/VariableScopeElResolver.java b/activiti-engine/src/main/java/org/activiti/engine/impl/el/VariableScopeElResolver.java index 80f1d09bcd8ead00329cd92a5647ffb1a9060e8c..124b8ec81803c89943c52910b87a3f7e26f67206 100644 --- a/activiti-engine/src/main/java/org/activiti/engine/impl/el/VariableScopeElResolver.java +++ b/activiti-engine/src/main/java/org/activiti/engine/impl/el/VariableScopeElResolver.java @@ -1,9 +1,9 @@ /* Licensed 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. @@ -28,89 +28,103 @@ import org.activiti.engine.impl.persistence.entity.VariableInstance; import com.fasterxml.jackson.databind.JsonNode; - - /** - * Implementation of an {@link ELResolver} that resolves expressions with the process variables of a given {@link VariableScope} as context.
- * Also exposes the currently logged in username to be used in expressions (if any) + * Implementation of an {@link ELResolver} that resolves expressions with the + * process variables of a given {@link VariableScope} as context.
+ * Also exposes the currently logged in username to be used in expressions (if + * any) + * + * * - - */ public class VariableScopeElResolver extends ELResolver { - public static final String EXECUTION_KEY = "execution"; - public static final String TASK_KEY = "task"; - public static final String LOGGED_IN_USER_KEY = "authenticatedUserId"; - - protected VariableScope variableScope; - - public VariableScopeElResolver(VariableScope variableScope) { - this.variableScope = variableScope; - } - - public Object getValue(ELContext context, Object base, Object property) { - - if (base == null) { - String variable = (String) property; // according to javadoc, can only be a String - - if ((EXECUTION_KEY.equals(property) && variableScope instanceof ExecutionEntity) || (TASK_KEY.equals(property) && variableScope instanceof TaskEntity)) { - context.setPropertyResolved(true); - return variableScope; - } else if (EXECUTION_KEY.equals(property) && variableScope instanceof TaskEntity) { - context.setPropertyResolved(true); - return ((TaskEntity) variableScope).getExecution(); - } else if (LOGGED_IN_USER_KEY.equals(property)) { - context.setPropertyResolved(true); - return Authentication.getAuthenticatedUserId(); - } else { - if (variableScope.hasVariable(variable)) { - context.setPropertyResolved(true); // if not set, the next elResolver in the CompositeElResolver will be called - VariableInstance variableInstance = variableScope.getVariableInstance(variable); - Object value = variableInstance.getValue(); - if ( ("json".equals(variableInstance.getTypeName()) || "longJson".equals(variableInstance.getTypeName())) && (value instanceof JsonNode) && ((JsonNode)value).isArray() ) { - return Context.getProcessEngineConfiguration().getObjectMapper().convertValue(value, List.class); - } else { - return value; - } - } - } - } - - // property resolution (eg. bean.value) will be done by the - // BeanElResolver (part of the CompositeElResolver) - // It will use the bean resolved in this resolver as base. - - return null; - } - - public boolean isReadOnly(ELContext context, Object base, Object property) { - if (base == null) { - String variable = (String) property; - return !variableScope.hasVariable(variable); - } - return true; - } - - public void setValue(ELContext context, Object base, Object property, Object value) { - if (base == null) { - String variable = (String) property; - if (variableScope.hasVariable(variable)) { - variableScope.setVariable(variable, value); - } - } - } - - public Class getCommonPropertyType(ELContext arg0, Object arg1) { - return Object.class; - } - - public Iterator getFeatureDescriptors(ELContext arg0, Object arg1) { - return null; - } - - public Class getType(ELContext arg0, Object arg1, Object arg2) { - return Object.class; - } + public static final String EXECUTION_KEY = "execution"; + public static final String TASK_KEY = "task"; + public static final String LOGGED_IN_USER_KEY = "authenticatedUserId"; + + protected VariableScope variableScope; + + public VariableScopeElResolver(VariableScope variableScope) { + this.variableScope = variableScope; + } + + @Override + public Object getValue(ELContext context, Object base, Object property) { + + if (base == null) { + String variable = (String) property; // according to javadoc, can + // only be a String + + if ((EXECUTION_KEY.equals(property) && variableScope instanceof ExecutionEntity) + || (TASK_KEY.equals(property) && variableScope instanceof TaskEntity)) { + context.setPropertyResolved(true); + return variableScope; + } else if (EXECUTION_KEY.equals(property) && variableScope instanceof TaskEntity) { + context.setPropertyResolved(true); + return ((TaskEntity) variableScope).getExecution(); + } else if (LOGGED_IN_USER_KEY.equals(property)) { + context.setPropertyResolved(true); + return Authentication.getAuthenticatedUserId(); + } else { + if (variableScope.hasVariable(variable)) { + context.setPropertyResolved(true); // if not set, the next + // elResolver in the + // CompositeElResolver + // will be called + VariableInstance variableInstance = variableScope.getVariableInstance(variable); + Object value = variableInstance.getValue(); + if (("json".equals(variableInstance.getTypeName()) + || "longJson".equals(variableInstance.getTypeName())) && (value instanceof JsonNode) + && ((JsonNode) value).isArray()) { + return Context.getProcessEngineConfiguration().getObjectMapper().convertValue(value, + List.class); + } else { + return value; + } + } + } + } + + // property resolution (eg. bean.value) will be done by the + // BeanElResolver (part of the CompositeElResolver) + // It will use the bean resolved in this resolver as base. + + return null; + } + + @Override + public boolean isReadOnly(ELContext context, Object base, Object property) { + if (base == null) { + String variable = (String) property; + return !variableScope.hasVariable(variable); + } + return true; + } + + @Override + public void setValue(ELContext context, Object base, Object property, Object value) { + if (base == null) { + String variable = (String) property; + if (variableScope.hasVariable(variable)) { + variableScope.setVariable(variable, value); + } + } + } + + @Override + public Class getCommonPropertyType(ELContext arg0, Object arg1) { + return Object.class; + } + + @Override + public Iterator getFeatureDescriptors(ELContext arg0, Object arg1) { + return null; + } + + @Override + public Class getType(ELContext arg0, Object arg1, Object arg2) { + return Object.class; + } }