initial typeDescriptor awareness in the EL. some basic testing of using GenericConversionService
This commit is contained in:
parent
65afc80821
commit
54865c0c1f
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package org.springframework.expression;
|
package org.springframework.expression;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A property accessor is able to read (and possibly write) to object properties. The interface places no restrictions
|
* A property accessor is able to read (and possibly write) to object properties. The interface places no restrictions
|
||||||
* and so implementors are free to access properties directly as fields or through getters or in any other way they see
|
* and so implementors are free to access properties directly as fields or through getters or in any other way they see
|
||||||
|
|
@ -42,6 +44,15 @@ public interface PropertyAccessor {
|
||||||
*/
|
*/
|
||||||
Class[] getSpecificTargetClasses();
|
Class[] getSpecificTargetClasses();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called to retrieve a type descriptor that describes the type of the property.
|
||||||
|
* @param context the evaluation context in which the access is being attempted
|
||||||
|
* @param target the target object upon which the property is being accessed
|
||||||
|
* @param name the name of the property being accessed
|
||||||
|
* @return a type descriptor that describes the type of this property.
|
||||||
|
*/
|
||||||
|
TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to determine if a resolver instance is able to access a specified property on a specified target object.
|
* Called to determine if a resolver instance is able to access a specified property on a specified target object.
|
||||||
* @param context the evaluation context in which the access is being attempted
|
* @param context the evaluation context in which the access is being attempted
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package org.springframework.expression;
|
package org.springframework.expression;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A type converter can convert values between different types encountered
|
* A type converter can convert values between different types encountered
|
||||||
* during expression evaluation.
|
* during expression evaluation.
|
||||||
|
|
@ -26,6 +28,7 @@ package org.springframework.expression;
|
||||||
public interface TypeConverter {
|
public interface TypeConverter {
|
||||||
// TODO replace this stuff with Keiths spring-binding conversion code
|
// TODO replace this stuff with Keiths spring-binding conversion code
|
||||||
// TODO should ExpressionException be thrown for lost precision in the case of coercion?
|
// TODO should ExpressionException be thrown for lost precision in the case of coercion?
|
||||||
|
// TODO could remove the methods where the target is Class and just keep the TypeDescriptor variants
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert (may coerce) a value from one type to another, for example from a boolean to a string.
|
* Convert (may coerce) a value from one type to another, for example from a boolean to a string.
|
||||||
|
|
@ -36,6 +39,17 @@ public interface TypeConverter {
|
||||||
*/
|
*/
|
||||||
<T> T convertValue(Object value, Class<T> targetType) throws EvaluationException;
|
<T> T convertValue(Object value, Class<T> targetType) throws EvaluationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert (may coerce) a value from one type to another, for example from a boolean to a string.
|
||||||
|
* The typeDescriptor parameter enables support for typed collections - if the caller really wishes they
|
||||||
|
* can have a List<Integer> for example, rather than simply a List.
|
||||||
|
* @param value the value to be converted
|
||||||
|
* @param typeDescriptor a type descriptor that supplies extra information about the requested result type
|
||||||
|
* @return the converted value
|
||||||
|
* @throws EvaluationException if conversion is not possible
|
||||||
|
*/
|
||||||
|
<T> T convertValue(Object value, TypeDescriptor typeDescriptor) throws EvaluationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if the type converter can convert the specified type to the desired target type.
|
* Return true if the type converter can convert the specified type to the desired target type.
|
||||||
* @param sourceType the type to be converted from
|
* @param sourceType the type to be converted from
|
||||||
|
|
@ -44,4 +58,12 @@ public interface TypeConverter {
|
||||||
*/
|
*/
|
||||||
boolean canConvert(Class<?> sourceType, Class<?> targetType);
|
boolean canConvert(Class<?> sourceType, Class<?> targetType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the type converter can convert the specified type to the desired target type.
|
||||||
|
* @param sourceType the type to be converted from
|
||||||
|
* @param typeDescriptor a type descriptor that supplies extra information about the requested result type
|
||||||
|
* @return true if that conversion can be performed
|
||||||
|
*/
|
||||||
|
boolean canConvert(Class<?> sourceType, TypeDescriptor typeDescriptor);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.expression.EvaluationContext;
|
import org.springframework.expression.EvaluationContext;
|
||||||
import org.springframework.expression.EvaluationException;
|
import org.springframework.expression.EvaluationException;
|
||||||
import org.springframework.expression.Operation;
|
import org.springframework.expression.Operation;
|
||||||
|
|
@ -99,6 +100,10 @@ public class ExpressionState {
|
||||||
return this.relatedContext.getTypeLocator().findType(type);
|
return this.relatedContext.getTypeLocator().findType(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T> T convertValue(Object value, TypeDescriptor targetTypeDescriptor) throws EvaluationException {
|
||||||
|
return this.relatedContext.getTypeConverter().convertValue(value, targetTypeDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
public <T> T convertValue(Object value, Class<T> targetType) throws EvaluationException {
|
public <T> T convertValue(Object value, Class<T> targetType) throws EvaluationException {
|
||||||
return this.relatedContext.getTypeConverter().convertValue(value, targetType);
|
return this.relatedContext.getTypeConverter().convertValue(value, targetType);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.antlr.runtime.Token;
|
import org.antlr.runtime.Token;
|
||||||
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.expression.EvaluationException;
|
import org.springframework.expression.EvaluationException;
|
||||||
import org.springframework.expression.spel.ExpressionState;
|
import org.springframework.expression.spel.ExpressionState;
|
||||||
import org.springframework.expression.spel.SpelException;
|
import org.springframework.expression.spel.SpelException;
|
||||||
|
|
@ -51,7 +52,7 @@ public class Indexer extends SpelNodeImpl {
|
||||||
return ((Map<?, ?>) ctx).get(index);
|
return ((Map<?, ?>) ctx).get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
int idx = state.convertValue(index, Integer.class);
|
int idx = state.convertValue(index, INTEGER_TYPE_DESCRIPTOR);
|
||||||
|
|
||||||
if (ctx.getClass().isArray()) {
|
if (ctx.getClass().isArray()) {
|
||||||
return accessArrayElement(ctx, idx);
|
return accessArrayElement(ctx, idx);
|
||||||
|
|
@ -109,7 +110,7 @@ public class Indexer extends SpelNodeImpl {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int idx = state.convertValue(index, Integer.class);
|
int idx = state.convertValue(index, INTEGER_TYPE_DESCRIPTOR);
|
||||||
|
|
||||||
if (ctx.getClass().isArray()) {
|
if (ctx.getClass().isArray()) {
|
||||||
setArrayElement(state, ctx, idx, newValue);
|
setArrayElement(state, ctx, idx, newValue);
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ package org.springframework.expression.spel.ast;
|
||||||
|
|
||||||
import org.antlr.runtime.Token;
|
import org.antlr.runtime.Token;
|
||||||
import org.springframework.expression.EvaluationException;
|
import org.springframework.expression.EvaluationException;
|
||||||
import org.springframework.expression.spel.SpelException;
|
|
||||||
import org.springframework.expression.spel.ExpressionState;
|
import org.springframework.expression.spel.ExpressionState;
|
||||||
|
import org.springframework.expression.spel.SpelException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the boolean AND operation.
|
* Represents the boolean AND operation.
|
||||||
|
|
@ -44,7 +44,7 @@ public class OperatorAnd extends Operator {
|
||||||
boolean rightValue;
|
boolean rightValue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
leftValue = state.convertValue(getLeftOperand().getValueInternal(state), Boolean.class);
|
leftValue = state.convertValue(getLeftOperand().getValueInternal(state), BOOLEAN_TYPE_DESCRIPTOR);
|
||||||
}
|
}
|
||||||
catch (SpelException ee) {
|
catch (SpelException ee) {
|
||||||
ee.setPosition(getLeftOperand().getCharPositionInLine());
|
ee.setPosition(getLeftOperand().getCharPositionInLine());
|
||||||
|
|
@ -56,7 +56,7 @@ public class OperatorAnd extends Operator {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
rightValue = state.convertValue(getRightOperand().getValueInternal(state), Boolean.class);
|
rightValue = state.convertValue(getRightOperand().getValueInternal(state), BOOLEAN_TYPE_DESCRIPTOR);
|
||||||
}
|
}
|
||||||
catch (SpelException ee) {
|
catch (SpelException ee) {
|
||||||
ee.setPosition(getRightOperand().getCharPositionInLine());
|
ee.setPosition(getRightOperand().getCharPositionInLine());
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ package org.springframework.expression.spel.ast;
|
||||||
|
|
||||||
import org.antlr.runtime.Token;
|
import org.antlr.runtime.Token;
|
||||||
import org.springframework.expression.EvaluationException;
|
import org.springframework.expression.EvaluationException;
|
||||||
import org.springframework.expression.spel.SpelException;
|
|
||||||
import org.springframework.expression.spel.ExpressionState;
|
import org.springframework.expression.spel.ExpressionState;
|
||||||
|
import org.springframework.expression.spel.SpelException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a NOT operation.
|
* Represents a NOT operation.
|
||||||
|
|
@ -36,7 +36,7 @@ public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do
|
||||||
@Override
|
@Override
|
||||||
public Object getValueInternal(ExpressionState state) throws EvaluationException {
|
public Object getValueInternal(ExpressionState state) throws EvaluationException {
|
||||||
try {
|
try {
|
||||||
boolean value = state.convertValue(getChild(0).getValueInternal(state), Boolean.class);
|
boolean value = state.convertValue(getChild(0).getValueInternal(state), BOOLEAN_TYPE_DESCRIPTOR);
|
||||||
return !value;
|
return !value;
|
||||||
}
|
}
|
||||||
catch (SpelException see) {
|
catch (SpelException see) {
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ package org.springframework.expression.spel.ast;
|
||||||
|
|
||||||
import org.antlr.runtime.Token;
|
import org.antlr.runtime.Token;
|
||||||
import org.springframework.expression.EvaluationException;
|
import org.springframework.expression.EvaluationException;
|
||||||
import org.springframework.expression.spel.SpelException;
|
|
||||||
import org.springframework.expression.spel.ExpressionState;
|
import org.springframework.expression.spel.ExpressionState;
|
||||||
|
import org.springframework.expression.spel.SpelException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the boolean OR operation.
|
* Represents the boolean OR operation.
|
||||||
|
|
@ -43,7 +43,7 @@ public class OperatorOr extends Operator {
|
||||||
boolean leftValue;
|
boolean leftValue;
|
||||||
boolean rightValue;
|
boolean rightValue;
|
||||||
try {
|
try {
|
||||||
leftValue = state.convertValue(getLeftOperand().getValueInternal(state), Boolean.class);
|
leftValue = state.convertValue(getLeftOperand().getValueInternal(state), BOOLEAN_TYPE_DESCRIPTOR);
|
||||||
}
|
}
|
||||||
catch (SpelException see) {
|
catch (SpelException see) {
|
||||||
see.setPosition(getLeftOperand().getCharPositionInLine());
|
see.setPosition(getLeftOperand().getCharPositionInLine());
|
||||||
|
|
@ -55,7 +55,7 @@ public class OperatorOr extends Operator {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
rightValue = state.convertValue(getRightOperand().getValueInternal(state), Boolean.class);
|
rightValue = state.convertValue(getRightOperand().getValueInternal(state), BOOLEAN_TYPE_DESCRIPTOR);
|
||||||
}
|
}
|
||||||
catch (SpelException see) {
|
catch (SpelException see) {
|
||||||
see.setPosition(getRightOperand().getCharPositionInLine());
|
see.setPosition(getRightOperand().getCharPositionInLine());
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,10 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.antlr.runtime.Token;
|
import org.antlr.runtime.Token;
|
||||||
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.expression.AccessException;
|
import org.springframework.expression.AccessException;
|
||||||
import org.springframework.expression.EvaluationContext;
|
import org.springframework.expression.EvaluationContext;
|
||||||
|
import org.springframework.expression.EvaluationException;
|
||||||
import org.springframework.expression.PropertyAccessor;
|
import org.springframework.expression.PropertyAccessor;
|
||||||
import org.springframework.expression.spel.ExpressionState;
|
import org.springframework.expression.spel.ExpressionState;
|
||||||
import org.springframework.expression.spel.SpelException;
|
import org.springframework.expression.spel.SpelException;
|
||||||
|
|
@ -42,6 +44,8 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
|
||||||
|
|
||||||
private volatile PropertyAccessor cachedWriteAccessor;
|
private volatile PropertyAccessor cachedWriteAccessor;
|
||||||
|
|
||||||
|
private volatile TypeDescriptor cachedTypeDescriptor;
|
||||||
|
|
||||||
|
|
||||||
public PropertyOrFieldReference(Token payload) {
|
public PropertyOrFieldReference(Token payload) {
|
||||||
super(payload);
|
super(payload);
|
||||||
|
|
@ -122,7 +126,19 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
|
||||||
PropertyAccessor accessorToUse = this.cachedWriteAccessor;
|
PropertyAccessor accessorToUse = this.cachedWriteAccessor;
|
||||||
if (accessorToUse != null) {
|
if (accessorToUse != null) {
|
||||||
try {
|
try {
|
||||||
accessorToUse.write(state.getEvaluationContext(), contextObject, name, newValue);
|
Object possiblyConvertedValue = newValue;
|
||||||
|
if (cachedTypeDescriptor == null) {
|
||||||
|
cachedTypeDescriptor=accessorToUse.getTypeDescriptor(eContext, contextObject, name);
|
||||||
|
}
|
||||||
|
if (cachedTypeDescriptor != null) {
|
||||||
|
try {
|
||||||
|
possiblyConvertedValue = state.convertValue(newValue, cachedTypeDescriptor.getType());
|
||||||
|
} catch (EvaluationException evaluationException) {
|
||||||
|
throw new SpelException(getCharPositionInLine(), evaluationException, SpelMessages.TYPE_CONVERSION_ERROR,
|
||||||
|
newValue.getClass(), cachedTypeDescriptor.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
accessorToUse.write(state.getEvaluationContext(), contextObject, name, possiblyConvertedValue);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
catch (AccessException ae) {
|
catch (AccessException ae) {
|
||||||
|
|
@ -140,7 +156,19 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
|
||||||
for (PropertyAccessor accessor : accessorsToTry) {
|
for (PropertyAccessor accessor : accessorsToTry) {
|
||||||
if (accessor.canWrite(eContext, contextObject, name)) {
|
if (accessor.canWrite(eContext, contextObject, name)) {
|
||||||
this.cachedWriteAccessor = accessor;
|
this.cachedWriteAccessor = accessor;
|
||||||
accessor.write(eContext, contextObject, name, newValue); // TODO missing conversion of newValue to the type of the property
|
Object possiblyConvertedValue = newValue;
|
||||||
|
if (cachedTypeDescriptor == null) {
|
||||||
|
cachedTypeDescriptor=accessor.getTypeDescriptor(eContext, contextObject, name);
|
||||||
|
}
|
||||||
|
if (cachedTypeDescriptor != null) {
|
||||||
|
try {
|
||||||
|
possiblyConvertedValue = state.convertValue(newValue, cachedTypeDescriptor);
|
||||||
|
} catch (EvaluationException evaluationException) {
|
||||||
|
throw new SpelException(getCharPositionInLine(), evaluationException, SpelMessages.TYPE_CONVERSION_ERROR,
|
||||||
|
newValue.getClass(), cachedTypeDescriptor.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
accessor.write(eContext, contextObject, name, possiblyConvertedValue);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import java.io.Serializable;
|
||||||
|
|
||||||
import org.antlr.runtime.Token;
|
import org.antlr.runtime.Token;
|
||||||
import org.antlr.runtime.tree.CommonTree;
|
import org.antlr.runtime.tree.CommonTree;
|
||||||
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.expression.EvaluationException;
|
import org.springframework.expression.EvaluationException;
|
||||||
import org.springframework.expression.common.ExpressionUtils;
|
import org.springframework.expression.common.ExpressionUtils;
|
||||||
import org.springframework.expression.spel.ExpressionState;
|
import org.springframework.expression.spel.ExpressionState;
|
||||||
|
|
@ -38,6 +38,9 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
*/
|
*/
|
||||||
public abstract class SpelNodeImpl extends CommonTree implements SpelNode, Serializable {
|
public abstract class SpelNodeImpl extends CommonTree implements SpelNode, Serializable {
|
||||||
|
|
||||||
|
protected static TypeDescriptor BOOLEAN_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Boolean.class);
|
||||||
|
protected static TypeDescriptor INTEGER_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Integer.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Antlr parser uses this constructor to build SpelNodes.
|
* The Antlr parser uses this constructor to build SpelNodes.
|
||||||
* @param payload the token for the node that has been parsed
|
* @param payload the token for the node that has been parsed
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ import java.lang.reflect.Modifier;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.expression.AccessException;
|
import org.springframework.expression.AccessException;
|
||||||
import org.springframework.expression.EvaluationContext;
|
import org.springframework.expression.EvaluationContext;
|
||||||
import org.springframework.expression.PropertyAccessor;
|
import org.springframework.expression.PropertyAccessor;
|
||||||
|
|
@ -32,9 +34,7 @@ import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple PropertyResolver that uses reflection to access properties for reading and writing. A property can be accessed
|
* Simple PropertyResolver that uses reflection to access properties for reading and writing. A property can be accessed
|
||||||
* if it is accessible as a field on the object or through a getter (if being read) or a setter (if being written). This
|
* if it is accessible as a field on the object or through a getter (if being read) or a setter (if being written).
|
||||||
* implementation currently follows the Resolver/Executor model (it extends CacheablePropertyAccessor) - the code that
|
|
||||||
* would be used if it were a simple property accessor is shown at the end.
|
|
||||||
*
|
*
|
||||||
* @author Andy Clement
|
* @author Andy Clement
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
|
@ -42,10 +42,11 @@ import org.springframework.util.StringUtils;
|
||||||
*/
|
*/
|
||||||
public class ReflectivePropertyResolver implements PropertyAccessor {
|
public class ReflectivePropertyResolver implements PropertyAccessor {
|
||||||
|
|
||||||
private final Map<CacheKey, Member> readerCache = new ConcurrentHashMap<CacheKey, Member>();
|
protected final Map<CacheKey, Member> readerCache = new ConcurrentHashMap<CacheKey, Member>();
|
||||||
|
|
||||||
private final Map<CacheKey, Member> writerCache = new ConcurrentHashMap<CacheKey, Member>();
|
protected final Map<CacheKey, Member> writerCache = new ConcurrentHashMap<CacheKey, Member>();
|
||||||
|
|
||||||
|
protected final Map<CacheKey, TypeDescriptor> typeDescriptorCache = new ConcurrentHashMap<CacheKey,TypeDescriptor>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return null which means this is a general purpose accessor
|
* @return null which means this is a general purpose accessor
|
||||||
|
|
@ -69,12 +70,14 @@ public class ReflectivePropertyResolver implements PropertyAccessor {
|
||||||
Method method = findGetterForProperty(name, type, target instanceof Class);
|
Method method = findGetterForProperty(name, type, target instanceof Class);
|
||||||
if (method != null) {
|
if (method != null) {
|
||||||
this.readerCache.put(cacheKey, method);
|
this.readerCache.put(cacheKey, method);
|
||||||
|
this.typeDescriptorCache.put(cacheKey, new TypeDescriptor(new MethodParameter(method,0)));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Field field = findField(name, type, target instanceof Class);
|
Field field = findField(name, type, target instanceof Class);
|
||||||
if (field != null) {
|
if (field != null) {
|
||||||
this.readerCache.put(cacheKey, field);
|
this.readerCache.put(cacheKey, field);
|
||||||
|
this.typeDescriptorCache.put(cacheKey, new TypeDescriptor(field));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -152,12 +155,14 @@ public class ReflectivePropertyResolver implements PropertyAccessor {
|
||||||
Method method = findSetterForProperty(name, type, target instanceof Class);
|
Method method = findSetterForProperty(name, type, target instanceof Class);
|
||||||
if (method != null) {
|
if (method != null) {
|
||||||
this.writerCache.put(cacheKey, method);
|
this.writerCache.put(cacheKey, method);
|
||||||
|
this.typeDescriptorCache.put(cacheKey, new TypeDescriptor(new MethodParameter(method,0)));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Field field = findField(name, type, target instanceof Class);
|
Field field = findField(name, type, target instanceof Class);
|
||||||
if (field != null) {
|
if (field != null) {
|
||||||
this.writerCache.put(cacheKey, field);
|
this.writerCache.put(cacheKey, field);
|
||||||
|
this.typeDescriptorCache.put(cacheKey, new TypeDescriptor(field));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -218,6 +223,31 @@ public class ReflectivePropertyResolver implements PropertyAccessor {
|
||||||
throw new AccessException("Neither setter nor field found for property '" + name + "'");
|
throw new AccessException("Neither setter nor field found for property '" + name + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) {
|
||||||
|
if (target == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Class<?> type = (target instanceof Class ? (Class<?>) target : target.getClass());
|
||||||
|
|
||||||
|
if (type.isArray() && name.equals("length")) {
|
||||||
|
return TypeDescriptor.valueOf(Integer.TYPE);
|
||||||
|
}
|
||||||
|
CacheKey cacheKey = new CacheKey(type, name);
|
||||||
|
TypeDescriptor typeDescriptor = this.typeDescriptorCache.get(cacheKey);
|
||||||
|
if (typeDescriptor == null) {
|
||||||
|
// attempt to populate the cache entry
|
||||||
|
try {
|
||||||
|
if (canRead(context, target, name)) {
|
||||||
|
typeDescriptor = this.typeDescriptorCache.get(cacheKey);
|
||||||
|
} else if (canWrite(context, target, name)) {
|
||||||
|
typeDescriptor = this.typeDescriptorCache.get(cacheKey);
|
||||||
|
}
|
||||||
|
} catch (AccessException e) {
|
||||||
|
// continue with null typeDescriptor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return typeDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a getter method for the specified property. A getter is defined as a method whose name start with the prefix
|
* Find a getter method for the specified property. A getter is defined as a method whose name start with the prefix
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.expression.spel.support;
|
package org.springframework.expression.spel.support;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.expression.EvaluationException;
|
import org.springframework.expression.EvaluationException;
|
||||||
import org.springframework.expression.TypeConverter;
|
import org.springframework.expression.TypeConverter;
|
||||||
import org.springframework.expression.spel.SpelException;
|
import org.springframework.expression.spel.SpelException;
|
||||||
|
|
@ -25,6 +26,7 @@ import org.springframework.util.NumberUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
* @author Andy Clement
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class StandardTypeConverter implements TypeConverter {
|
public class StandardTypeConverter implements TypeConverter {
|
||||||
|
|
@ -74,6 +76,11 @@ public class StandardTypeConverter implements TypeConverter {
|
||||||
throw new SpelException(SpelMessages.TYPE_CONVERSION_ERROR, value.getClass(), targetType);
|
throw new SpelException(SpelMessages.TYPE_CONVERSION_ERROR, value.getClass(), targetType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T convertValue(Object value, TypeDescriptor typeDescriptor) throws EvaluationException {
|
||||||
|
return (T)convertValue(value,typeDescriptor.getType());
|
||||||
|
}
|
||||||
|
|
||||||
public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
|
public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
|
||||||
if (ClassUtils.isAssignable(targetType, sourceType) || String.class.equals(targetType)) {
|
if (ClassUtils.isAssignable(targetType, sourceType) || String.class.equals(targetType)) {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -84,4 +91,8 @@ public class StandardTypeConverter implements TypeConverter {
|
||||||
(Boolean.class.equals(actualTargetType) && String.class.equals(sourceType)));
|
(Boolean.class.equals(actualTargetType) && String.class.equals(sourceType)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean canConvert(Class<?> sourceType, TypeDescriptor typeDescriptor) {
|
||||||
|
return canConvert(sourceType,typeDescriptor.getType());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,14 @@
|
||||||
|
|
||||||
package org.springframework.expression.spel;
|
package org.springframework.expression.spel;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.Color;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.expression.AccessException;
|
import org.springframework.expression.AccessException;
|
||||||
import org.springframework.expression.EvaluationContext;
|
import org.springframework.expression.EvaluationContext;
|
||||||
import org.springframework.expression.EvaluationException;
|
import org.springframework.expression.EvaluationException;
|
||||||
|
|
@ -264,6 +265,10 @@ public class ExpressionLanguageScenarioTests extends ExpressionTestCase {
|
||||||
public void write(EvaluationContext context, Object target, String name, Object newValue)
|
public void write(EvaluationContext context, Object target, String name, Object newValue)
|
||||||
throws AccessException {
|
throws AccessException {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -301,6 +306,9 @@ public class ExpressionLanguageScenarioTests extends ExpressionTestCase {
|
||||||
|
|
||||||
public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException {
|
public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException {
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
public TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2009 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.expression.spel;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.ConversionExecutor;
|
||||||
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
|
import org.springframework.core.convert.service.DefaultConversionService;
|
||||||
|
import org.springframework.core.convert.service.GenericConversionService;
|
||||||
|
import org.springframework.expression.EvaluationException;
|
||||||
|
import org.springframework.expression.Expression;
|
||||||
|
import org.springframework.expression.TypeConverter;
|
||||||
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expression evaluation where the TypeConverter plugged in is the {@link GenericConversionService}
|
||||||
|
*
|
||||||
|
* @author Andy Clement
|
||||||
|
*/
|
||||||
|
public class ExpressionTestsUsingCoreConversionService extends ExpressionTestCase {
|
||||||
|
|
||||||
|
private static List<String> listOfString = new ArrayList<String>();
|
||||||
|
private static TypeDescriptor typeDescriptorForListOfString = null;
|
||||||
|
private static List<Integer> listOfInteger = new ArrayList<Integer>();
|
||||||
|
private static TypeDescriptor typeDescriptorForListOfInteger = null;
|
||||||
|
|
||||||
|
static {
|
||||||
|
listOfString.add("1");
|
||||||
|
listOfString.add("2");
|
||||||
|
listOfString.add("3");
|
||||||
|
listOfInteger.add(4);
|
||||||
|
listOfInteger.add(5);
|
||||||
|
listOfInteger.add(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
typeDescriptorForListOfString = new TypeDescriptor(ExpressionTestsUsingCoreConversionService.class.getDeclaredField("listOfString"));
|
||||||
|
typeDescriptorForListOfInteger = new TypeDescriptor(ExpressionTestsUsingCoreConversionService.class.getDeclaredField("listOfInteger"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the service can convert what we are about to use in the expression evaluation tests.
|
||||||
|
*/
|
||||||
|
public void testConversionsAvailable() throws Exception {
|
||||||
|
TypeConvertorUsingConversionService tcs = new TypeConvertorUsingConversionService();
|
||||||
|
|
||||||
|
// ArrayList containing List<Integer> to List<String>
|
||||||
|
Class<?> clazz = typeDescriptorForListOfString.getElementType();
|
||||||
|
assertEquals(String.class,clazz);
|
||||||
|
ConversionExecutor executor = tcs.getConversionExecutor(ArrayList.class, typeDescriptorForListOfString);
|
||||||
|
assertNotNull(executor);
|
||||||
|
List l = (List)executor.execute(listOfInteger);
|
||||||
|
assertNotNull(l);
|
||||||
|
|
||||||
|
// ArrayList containing List<String> to List<Integer>
|
||||||
|
clazz = typeDescriptorForListOfInteger.getElementType();
|
||||||
|
assertEquals(Integer.class,clazz);
|
||||||
|
executor = tcs.getConversionExecutor(ArrayList.class, typeDescriptorForListOfInteger);
|
||||||
|
assertNotNull(executor);
|
||||||
|
l = (List)executor.execute(listOfString);
|
||||||
|
assertNotNull(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSetParameterizedList() throws Exception {
|
||||||
|
StandardEvaluationContext context = TestScenarioCreator.getTestEvaluationContext();
|
||||||
|
Expression e = parser.parseExpression("listOfInteger.size()");
|
||||||
|
assertEquals(0,e.getValue(context,Integer.class).intValue());
|
||||||
|
context.setTypeConverter(new TypeConvertorUsingConversionService());
|
||||||
|
// Assign a List<String> to the List<Integer> field - the component elements should be converted
|
||||||
|
parser.parseExpression("listOfInteger").setValue(context,listOfString);
|
||||||
|
assertEquals(3,e.getValue(context,Integer.class).intValue()); // size now 3
|
||||||
|
Class clazz = parser.parseExpression("listOfInteger[1].getClass()").getValue(context,Class.class); // element type correctly Integer
|
||||||
|
assertEquals(Integer.class,clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type convertor that uses the core conversion service.
|
||||||
|
*/
|
||||||
|
private static class TypeConvertorUsingConversionService extends DefaultConversionService implements TypeConverter {
|
||||||
|
|
||||||
|
public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
|
||||||
|
return super.canConvert(sourceType, TypeDescriptor.valueOf(targetType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canConvert(Class<?> sourceType, TypeDescriptor typeDescriptor) {
|
||||||
|
return super.canConvert(sourceType, typeDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T convertValue(Object value, Class<T> targetType) throws EvaluationException {
|
||||||
|
return (T)super.executeConversion(value,TypeDescriptor.valueOf(targetType));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T convertValue(Object value, TypeDescriptor typeDescriptor)
|
||||||
|
throws EvaluationException {
|
||||||
|
return (T)super.executeConversion(value, typeDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -18,6 +18,7 @@ package org.springframework.expression.spel;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.expression.AccessException;
|
import org.springframework.expression.AccessException;
|
||||||
import org.springframework.expression.EvaluationContext;
|
import org.springframework.expression.EvaluationContext;
|
||||||
import org.springframework.expression.Expression;
|
import org.springframework.expression.Expression;
|
||||||
|
|
@ -61,7 +62,7 @@ public class MapAccessTests extends ExpressionTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class MapAccessor implements PropertyAccessor {
|
public static class MapAccessor implements PropertyAccessor {
|
||||||
|
|
||||||
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
|
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
|
||||||
return (((Map) target).containsKey(name));
|
return (((Map) target).containsKey(name));
|
||||||
|
|
@ -75,6 +76,7 @@ public class MapAccessTests extends ExpressionTestCase {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public void write(EvaluationContext context, Object target, String name, Object newValue)
|
public void write(EvaluationContext context, Object target, String name, Object newValue)
|
||||||
throws AccessException {
|
throws AccessException {
|
||||||
((Map) target).put(name, newValue);
|
((Map) target).put(name, newValue);
|
||||||
|
|
@ -83,6 +85,10 @@ public class MapAccessTests extends ExpressionTestCase {
|
||||||
public Class<?>[] getSpecificTargetClasses() {
|
public Class<?>[] getSpecificTargetClasses() {
|
||||||
return new Class[] { Map.class };
|
return new Class[] { Map.class };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) {
|
||||||
|
return TypeDescriptor.valueOf(Map.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.expression.spel;
|
package org.springframework.expression.spel;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.expression.AccessException;
|
import org.springframework.expression.AccessException;
|
||||||
import org.springframework.expression.EvaluationContext;
|
import org.springframework.expression.EvaluationContext;
|
||||||
import org.springframework.expression.EvaluationException;
|
import org.springframework.expression.EvaluationException;
|
||||||
|
|
@ -87,6 +88,10 @@ public class PropertyAccessTests extends ExpressionTestCase {
|
||||||
|
|
||||||
int flibbles = 7;
|
int flibbles = 7;
|
||||||
|
|
||||||
|
public TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public Class<?>[] getSpecificTargetClasses() {
|
public Class<?>[] getSpecificTargetClasses() {
|
||||||
return new Class[] { String.class };
|
return new Class[] { String.class };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package org.springframework.expression.spel;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.expression.AccessException;
|
import org.springframework.expression.AccessException;
|
||||||
import org.springframework.expression.EvaluationContext;
|
import org.springframework.expression.EvaluationContext;
|
||||||
import org.springframework.expression.EvaluationException;
|
import org.springframework.expression.EvaluationException;
|
||||||
|
|
@ -224,6 +225,11 @@ public class ScenariosForSpringSecurity extends ExpressionTestCase {
|
||||||
public Class<?>[] getSpecificTargetClasses() {
|
public Class<?>[] getSpecificTargetClasses() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -252,6 +258,10 @@ public class ScenariosForSpringSecurity extends ExpressionTestCase {
|
||||||
public Class<?>[] getSpecificTargetClasses() {
|
public Class<?>[] getSpecificTargetClasses() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,10 +98,13 @@ public class SetValueTests extends ExpressionTestCase {
|
||||||
setValueExpectError("'hello'[3]", 'p');
|
setValueExpectError("'hello'[3]", 'p');
|
||||||
}
|
}
|
||||||
|
|
||||||
// public void testSetPropertyTypeCoersion() {
|
public void testSetPropertyTypeCoersion() {
|
||||||
// setValue("publicBoolean", "true");
|
setValue("publicBoolean", "true", Boolean.TRUE);
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
public void testSetPropertyTypeCoersionThroughSetter() {
|
||||||
|
setValue("SomeProperty", "true", Boolean.TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call setValue() but expect it to fail.
|
* Call setValue() but expect it to fail.
|
||||||
|
|
@ -116,7 +119,6 @@ public class SetValueTests extends ExpressionTestCase {
|
||||||
SpelUtilities.printAbstractSyntaxTree(System.out, e);
|
SpelUtilities.printAbstractSyntaxTree(System.out, e);
|
||||||
}
|
}
|
||||||
StandardEvaluationContext lContext = TestScenarioCreator.getTestEvaluationContext();
|
StandardEvaluationContext lContext = TestScenarioCreator.getTestEvaluationContext();
|
||||||
// assertTrue("Expression is not writeable but should be", e.isWritable(lContext));
|
|
||||||
e.setValue(lContext, value);
|
e.setValue(lContext, value);
|
||||||
fail("expected an error");
|
fail("expected an error");
|
||||||
} catch (ParseException pe) {
|
} catch (ParseException pe) {
|
||||||
|
|
@ -148,4 +150,30 @@ public class SetValueTests extends ExpressionTestCase {
|
||||||
fail("Unexpected Exception: " + pe.getMessage());
|
fail("Unexpected Exception: " + pe.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For use when coercion is happening during a setValue(). The expectedValue should be
|
||||||
|
* the coerced form of the value.
|
||||||
|
*/
|
||||||
|
protected void setValue(String expression, Object value, Object expectedValue) {
|
||||||
|
try {
|
||||||
|
Expression e = parser.parseExpression(expression);
|
||||||
|
if (e == null) {
|
||||||
|
fail("Parser returned null for expression");
|
||||||
|
}
|
||||||
|
if (DEBUG) {
|
||||||
|
SpelUtilities.printAbstractSyntaxTree(System.out, e);
|
||||||
|
}
|
||||||
|
StandardEvaluationContext lContext = TestScenarioCreator.getTestEvaluationContext();
|
||||||
|
assertTrue("Expression is not writeable but should be", e.isWritable(lContext));
|
||||||
|
e.setValue(lContext, value);
|
||||||
|
assertEquals("Retrieved value was not equal to set value", expectedValue, e.getValue(lContext));
|
||||||
|
} catch (EvaluationException ee) {
|
||||||
|
ee.printStackTrace();
|
||||||
|
fail("Unexpected Exception: " + ee.getMessage());
|
||||||
|
} catch (ParseException pe) {
|
||||||
|
pe.printStackTrace();
|
||||||
|
fail("Unexpected Exception: " + pe.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ public class Inventor {
|
||||||
private List<PlaceOfBirth> placesLivedList = new ArrayList<PlaceOfBirth>();
|
private List<PlaceOfBirth> placesLivedList = new ArrayList<PlaceOfBirth>();
|
||||||
public ArrayContainer arrayContainer;
|
public ArrayContainer arrayContainer;
|
||||||
public boolean publicBoolean;
|
public boolean publicBoolean;
|
||||||
|
private boolean accessedThroughGetSet;
|
||||||
|
public List<Integer> listOfInteger = new ArrayList<Integer>();
|
||||||
|
|
||||||
public Inventor(String name, Date birthdate, String nationality) {
|
public Inventor(String name, Date birthdate, String nationality) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
@ -110,4 +112,12 @@ public class Inventor {
|
||||||
public Inventor(String... strings) {
|
public Inventor(String... strings) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getSomeProperty() {
|
||||||
|
return accessedThroughGetSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSomeProperty(boolean b) {
|
||||||
|
this.accessedThroughGetSet = b;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue