blob: ee8e392815757547f7daaecd1f2581050d775280 [file] [log] [blame]
/*
* 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.interceptor;
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;
import java.util.HashMap;
import java.util.Map;
/**
* <!-- START SNIPPET: description -->
* ConversionErrorInterceptor adds conversion errors from the ActionContext to the Action's field errors.
*
* <p>
* This interceptor adds any error found in the {@link ActionContext}'s conversionErrors map as a field error (provided
* that the action implements {@link ValidationAware}). In addition, any field that contains a validation error has its
* original value saved such that any subsequent requests for that value return the original value rather than the value
* in the action. This is important because if the value "abc" is submitted and can't be converted to an int, we want to
* display the original string ("abc") again rather than the int value (likely 0, which would make very little sense to
* the user).
* </p>
*
* <p>
* <b>Note:</b> Since 2.5.2, this interceptor extends {@link MethodFilterInterceptor}, therefore being
* able to deal with excludeMethods / includeMethods parameters. See [Workflow Interceptor]
* (class {@link DefaultWorkflowInterceptor}) for documentation and examples on how to use this feature.
* </p>
*
* <!-- END SNIPPET: description -->
*
* <p><u>Interceptor parameters:</u></p>
*
* <!-- START SNIPPET: parameters -->
*
* <ul>
* <li>None</li>
* </ul>
*
* <!-- END SNIPPET: parameters -->
*
* <p> <u>Extending the interceptor:</u></p>
*
* <!-- START SNIPPET: extending -->
*
* Because this interceptor is not web-specific, it abstracts the logic for whether an error should be added. This
* allows for web-specific interceptors to use more complex logic in the {@link #shouldAddError} method for when a value
* has a conversion error but is null or empty or otherwise indicates that the value was never actually entered by the
* user.
*
* <!-- END SNIPPET: extending -->
*
* <p> <u>Example code:</u></p>
*
* <pre>
* <!-- START SNIPPET: example -->
* &lt;action name="someAction" class="com.examples.SomeAction"&gt;
* &lt;interceptor-ref name="params"/&gt;
* &lt;interceptor-ref name="conversionError"/&gt;
* &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
* &lt;/action&gt;
* <!-- END SNIPPET: example -->
* </pre>
*
* @author Jason Carreira
*/
public class ConversionErrorInterceptor extends MethodFilterInterceptor {
public static final String ORIGINAL_PROPERTY_OVERRIDE = "original.property.override";
protected Object getOverrideExpr(ActionInvocation invocation, Object value) {
return escape(value);
}
protected String escape(Object value) {
return "\"" + StringEscapeUtils.escapeJava(String.valueOf(value)) + "\"";
}
@Override
public String doIntercept(ActionInvocation invocation) throws Exception {
ActionContext invocationContext = invocation.getInvocationContext();
Map<String, ConversionData> conversionErrors = invocationContext.getConversionErrors();
ValueStack stack = invocationContext.getValueStack();
HashMap<Object, Object> fakie = null;
for (Map.Entry<String, ConversionData> entry : conversionErrors.entrySet()) {
String propertyName = entry.getKey();
ConversionData conversionData = entry.getValue();
if (shouldAddError(propertyName, conversionData.getValue())) {
String message = XWorkConverter.getConversionErrorMessage(propertyName, conversionData.getToClass(), stack);
Object action = invocation.getAction();
if (action instanceof ValidationAware) {
ValidationAware va = (ValidationAware) action;
va.addFieldError(propertyName, message);
}
if (fakie == null) {
fakie = new HashMap<>();
}
fakie.put(propertyName, getOverrideExpr(invocation, conversionData.getValue()));
}
}
if (fakie != null) {
// if there were some errors, put the original (fake) values in place right before the result
stack.getContext().put(ORIGINAL_PROPERTY_OVERRIDE, fakie);
invocation.addPreResultListener(new PreResultListener() {
public void beforeResult(ActionInvocation invocation, String resultCode) {
Map<Object, Object> fakie = (Map<Object, Object>) invocation.getInvocationContext().get(ORIGINAL_PROPERTY_OVERRIDE);
if (fakie != null) {
invocation.getStack().setExprOverrides(fakie);
}
}
});
}
return invocation.invoke();
}
protected boolean shouldAddError(String propertyName, Object value) {
return true;
}
}