Skip to content

Commit

Permalink
Merge pull request #215 from aleksandr-m/feature/WW-685
Browse files Browse the repository at this point in the history
WW-685 Add generic type conversion error message
  • Loading branch information
lukaszlenart committed Mar 16, 2018
2 parents 7f3c09c + 1fcd1ae commit b492eaf
Show file tree
Hide file tree
Showing 17 changed files with 206 additions and 61 deletions.
7 changes: 4 additions & 3 deletions core/src/main/java/com/opensymphony/xwork2/ActionContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package com.opensymphony.xwork2;

import com.opensymphony.xwork2.conversion.impl.ConversionData;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.struts2.dispatcher.HttpParameters;
Expand Down Expand Up @@ -192,7 +193,7 @@ public Map<String, Object> getContextMap() {
*
* @param conversionErrors a Map of errors which occurred when executing the action.
*/
public void setConversionErrors(Map<String, Object> conversionErrors) {
public void setConversionErrors(Map<String, ConversionData> conversionErrors) {
put(CONVERSION_ERRORS, conversionErrors);
}

Expand All @@ -202,8 +203,8 @@ public void setConversionErrors(Map<String, Object> conversionErrors) {
* @return the map of conversion errors which occurred when executing the action or an empty map if
* there were no errors.
*/
public Map<String, Object> getConversionErrors() {
Map<String, Object> errors = (Map) get(CONVERSION_ERRORS);
public Map<String, ConversionData> getConversionErrors() {
Map<String, ConversionData> errors = (Map) get(CONVERSION_ERRORS);

if (errors == null) {
errors = new HashMap<>();
Expand Down
5 changes: 3 additions & 2 deletions core/src/main/java/com/opensymphony/xwork2/ActionSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package com.opensymphony.xwork2;

import com.opensymphony.xwork2.conversion.impl.ConversionData;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.ValidationAware;
Expand Down Expand Up @@ -131,9 +132,9 @@ public String getText(String key, String defaultValue, String[] args, ValueStack
* @return formatted expr with format specified by key
*/
public String getFormatted(String key, String expr) {
Map<String, Object> conversionErrors = ActionContext.getContext().getConversionErrors();
Map<String, ConversionData> conversionErrors = ActionContext.getContext().getConversionErrors();
if (conversionErrors.containsKey(expr)) {
String[] vals = (String[]) conversionErrors.get(expr);
String[] vals = (String[]) conversionErrors.get(expr).getValue();
return vals[0];
} else {
final ValueStack valueStack = ActionContext.getContext().getValueStack();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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 com.opensymphony.xwork2.conversion.impl;

public class ConversionData {
private Object value;
private Class toClass;

public ConversionData() {
}

public ConversionData(Object value, Class toClass) {
super();
this.value = value;
this.toClass = toClass;
}

public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public Class getToClass() {
return toClass;
}
public void setToClass(Class toClass) {
this.toClass = toClass;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ public void setTypeConverterHolder(TypeConverterHolder converterHolder) {
this.converterHolder = converterHolder;
}

public static String getConversionErrorMessage(String propertyName, ValueStack stack) {
public static String getConversionErrorMessage(String propertyName, Class toClass, ValueStack stack) {
LocalizedTextProvider localizedTextProvider = ActionContext.getContext().getContainer().getInstance(LocalizedTextProvider.class);
String defaultMessage = localizedTextProvider.findDefaultText("xwork.default.invalid.fieldvalue",
ActionContext.getContext().getLocale(),
Expand All @@ -201,9 +201,15 @@ public static String getConversionErrorMessage(String propertyName, ValueStack s

propertyName = removeAllIndexesInPropertyName(propertyName);

String getTextExpression = "getText('" + CONVERSION_ERROR_PROPERTY_PREFIX + propertyName + "','" + defaultMessage + "')";
String prefixedPropertyName = CONVERSION_ERROR_PROPERTY_PREFIX + propertyName;
String getTextExpression = "getText('" + prefixedPropertyName + "')";
String message = (String) stack.findValue(getTextExpression);

if (message == null || prefixedPropertyName.equals(message)) {
getTextExpression = "getText('" + CONVERSION_ERROR_PROPERTY_PREFIX + toClass.getName() + "','" + defaultMessage + "')";
message = (String) stack.findValue(getTextExpression);
}

if (message == null) {
message = defaultMessage;
} else {
Expand Down Expand Up @@ -308,7 +314,7 @@ public Object convertValue(Map<String, Object> context, Object target, Member me
return tc.convertValue(context, target, member, property, value, toClass);
} catch (Exception e) {
LOG.debug("Unable to convert value using type converter [{}]", tc.getClass().getName(), e);
handleConversionException(context, property, value, target);
handleConversionException(context, property, value, target, toClass);

return TypeConverter.NO_CONVERSION_POSSIBLE;
}
Expand All @@ -320,7 +326,7 @@ public Object convertValue(Map<String, Object> context, Object target, Member me
return defaultTypeConverter.convertValue(context, target, member, property, value, toClass);
} catch (Exception e) {
LOG.debug("Unable to convert value using type converter [{}]", defaultTypeConverter.getClass().getName(), e);
handleConversionException(context, property, value, target);
handleConversionException(context, property, value, target, toClass);

return TypeConverter.NO_CONVERSION_POSSIBLE;
}
Expand All @@ -330,7 +336,7 @@ public Object convertValue(Map<String, Object> context, Object target, Member me
return super.convertValue(value, toClass);
} catch (Exception e) {
LOG.debug("Unable to convert value using type converter [{}]", super.getClass().getName(), e);
handleConversionException(context, property, value, target);
handleConversionException(context, property, value, target, toClass);

return TypeConverter.NO_CONVERSION_POSSIBLE;
}
Expand Down Expand Up @@ -426,7 +432,7 @@ protected Object getConverter(Class clazz, String property) {
return null;
}

protected void handleConversionException(Map<String, Object> context, String property, Object value, Object object) {
protected void handleConversionException(Map<String, Object> context, String property, Object value, Object object, Class toClass) {
if (context != null && (Boolean.TRUE.equals(context.get(REPORT_CONVERSION_ERRORS)))) {
String realProperty = property;
String fullName = (String) context.get(CONVERSION_PROPERTY_FULLNAME);
Expand All @@ -435,14 +441,14 @@ protected void handleConversionException(Map<String, Object> context, String pro
realProperty = fullName;
}

Map<String, Object> conversionErrors = (Map<String, Object>) context.get(ActionContext.CONVERSION_ERRORS);
Map<String, ConversionData> conversionErrors = (Map<String, ConversionData>) context.get(ActionContext.CONVERSION_ERRORS);

if (conversionErrors == null) {
conversionErrors = new HashMap<>();
context.put(ActionContext.CONVERSION_ERRORS, conversionErrors);
}

conversionErrors.put(realProperty, value);
conversionErrors.put(realProperty, new ConversionData(value, toClass));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.conversion.impl.ConversionData;
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.commons.text.StringEscapeUtils;
Expand Down Expand Up @@ -100,17 +101,17 @@ protected String escape(Object value) {
public String doIntercept(ActionInvocation invocation) throws Exception {

ActionContext invocationContext = invocation.getInvocationContext();
Map<String, Object> conversionErrors = invocationContext.getConversionErrors();
Map<String, ConversionData> conversionErrors = invocationContext.getConversionErrors();
ValueStack stack = invocationContext.getValueStack();

HashMap<Object, Object> fakie = null;

for (Map.Entry<String, Object> entry : conversionErrors.entrySet()) {
for (Map.Entry<String, ConversionData> entry : conversionErrors.entrySet()) {
String propertyName = entry.getKey();
Object value = entry.getValue();
ConversionData conversionData = entry.getValue();

if (shouldAddError(propertyName, value)) {
String message = XWorkConverter.getConversionErrorMessage(propertyName, stack);
if (shouldAddError(propertyName, conversionData.getValue())) {
String message = XWorkConverter.getConversionErrorMessage(propertyName, conversionData.getToClass(), stack);

Object action = invocation.getAction();
if (action instanceof ValidationAware) {
Expand All @@ -122,7 +123,7 @@ public String doIntercept(ActionInvocation invocation) throws Exception {
fakie = new HashMap<>();
}

fakie.put(propertyName, getOverrideExpr(invocation, value));
fakie.put(propertyName, getOverrideExpr(invocation, conversionData.getValue()));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package com.opensymphony.xwork2.validator.validators;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.conversion.impl.ConversionData;
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.validator.ValidationException;
import org.apache.commons.lang3.StringUtils;
Expand Down Expand Up @@ -70,11 +71,11 @@ public void doValidate(Object object) throws ValidationException {
String fieldName = getFieldName();
String fullFieldName = getValidatorContext().getFullFieldName(fieldName);
ActionContext context = ActionContext.getContext();
Map<String, Object> conversionErrors = context.getConversionErrors();
Map<String, ConversionData> conversionErrors = context.getConversionErrors();

if (conversionErrors.containsKey(fullFieldName)) {
if (StringUtils.isBlank(defaultMessage)) {
defaultMessage = XWorkConverter.getConversionErrorMessage(fullFieldName, context.getValueStack());
defaultMessage = XWorkConverter.getConversionErrorMessage(fullFieldName, conversionErrors.get(fullFieldName).getToClass(), context.getValueStack());
}

addFieldError(fieldName, object);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.conversion.impl.ConversionData;
import com.opensymphony.xwork2.interceptor.PreResultListener;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.validator.ValidationException;
Expand Down Expand Up @@ -155,12 +156,12 @@ public void validate(Object object) throws ValidationException {
public void repopulateField(Object object) throws ValidationException {

ActionInvocation invocation = ActionContext.getContext().getActionInvocation();
Map<String, Object> conversionErrors = ActionContext.getContext().getConversionErrors();
Map<String, ConversionData> conversionErrors = ActionContext.getContext().getConversionErrors();

String fieldName = getFieldName();
String fullFieldName = getValidatorContext().getFullFieldName(fieldName);
if (conversionErrors.containsKey(fullFieldName)) {
Object value = conversionErrors.get(fullFieldName);
Object value = conversionErrors.get(fullFieldName).getValue();

final Map<Object, Object> fakeParams = new LinkedHashMap<Object, Object>();
boolean doExprOverride = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package com.opensymphony.xwork2;

import com.opensymphony.xwork2.conversion.impl.ConversionData;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.ValueStackFactory;
import org.apache.struts2.dispatcher.HttpParameters;
Expand Down Expand Up @@ -97,11 +98,11 @@ public void testParameters() {
}

public void testConversionErrors() {
Map<String, Object> errors = context.getConversionErrors();
Map<String, ConversionData> errors = context.getConversionErrors();
assertNotNull(errors);
assertEquals(0, errors.size());

Map<String, Object> errors2 = new HashMap<>();
Map<String, ConversionData> errors2 = new HashMap<>();
context.setConversionErrors(errors);
assertEquals(errors2, context.getConversionErrors());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package com.opensymphony.xwork2;

import com.opensymphony.xwork2.conversion.impl.ConversionData;
import com.opensymphony.xwork2.util.ValueStack;

import java.util.*;
Expand Down Expand Up @@ -303,7 +304,7 @@ public void testFormattingSupport() throws Exception {
}

public void testFormattingSupportWithConversionError() throws Exception {
ActionContext.getContext().getConversionErrors().put("val", new String[]{"4567def"});
ActionContext.getContext().getConversionErrors().put("val", new ConversionData(new String[]{"4567def"}, Double.class));
ActionContext.getContext().setLocale(new Locale("da"));
MyActionSupport mas = new MyActionSupport();
container.inject(mas);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,10 @@ public void testFieldErrorMessageAddedForComplexProperty() {
assertEquals("Conversion should have failed.", OgnlRuntime.NoConversionPossible, converter.convertValue(ognlStackContext, action.getBean(), null, "birth", value, Date.class));
stack.pop();

Map conversionErrors = (Map) stack.getContext().get(ActionContext.CONVERSION_ERRORS);
Map<String, ConversionData> conversionErrors = (Map<String, ConversionData>) stack.getContext().get(ActionContext.CONVERSION_ERRORS);
assertNotNull(conversionErrors);
assertTrue(conversionErrors.size() == 1);
assertEquals(value, conversionErrors.get("bean.birth"));
assertEquals(value, conversionErrors.get("bean.birth").getValue());
}

public void testFieldErrorMessageAddedWhenConversionFails() {
Expand All @@ -132,11 +132,11 @@ public void testFieldErrorMessageAddedWhenConversionFails() {
assertEquals("Conversion should have failed.", OgnlRuntime.NoConversionPossible, converter.convertValue(ognlStackContext, action, null, "date", value, Date.class));
stack.pop();

Map conversionErrors = (Map) ognlStackContext.get(ActionContext.CONVERSION_ERRORS);
Map<String, ConversionData> conversionErrors = (Map<String, ConversionData>) ognlStackContext.get(ActionContext.CONVERSION_ERRORS);
assertNotNull(conversionErrors);
assertEquals(1, conversionErrors.size());
assertNotNull(conversionErrors.get("date"));
assertEquals(value, conversionErrors.get("date"));
assertEquals(value, conversionErrors.get("date").getValue());
}

public void testFieldErrorMessageAddedWhenConversionFailsOnModelDriven() {
Expand All @@ -153,11 +153,11 @@ public void testFieldErrorMessageAddedWhenConversionFailsOnModelDriven() {
stack.pop();
stack.pop();

Map conversionErrors = (Map) ognlStackContext.get(ActionContext.CONVERSION_ERRORS);
Map<String, ConversionData> conversionErrors = (Map<String, ConversionData>) ognlStackContext.get(ActionContext.CONVERSION_ERRORS);
assertNotNull(conversionErrors);
assertEquals(1, conversionErrors.size());
assertNotNull(conversionErrors.get("birth"));
assertEquals(value, conversionErrors.get("birth"));
assertEquals(value, conversionErrors.get("birth").getValue());
}

public void testFindConversionErrorMessage() {
Expand All @@ -168,11 +168,11 @@ public void testFindConversionErrorMessage() {
stack.push(action);
stack.push(action.getModel());

String message = XWorkConverter.getConversionErrorMessage("birth", stack);
String message = XWorkConverter.getConversionErrorMessage("birth", Integer.class, stack);
assertNotNull(message);
assertEquals("Invalid date for birth.", message);

message = XWorkConverter.getConversionErrorMessage("foo", stack);
message = XWorkConverter.getConversionErrorMessage("foo", Integer.class, stack);
assertNotNull(message);
assertEquals("Invalid field value for field \"foo\".", message);
}
Expand Down

0 comments on commit b492eaf

Please sign in to comment.