ValidatorAction.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.validator;

  18. import java.io.BufferedReader;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.io.InputStreamReader;
  22. import java.io.Serializable;
  23. import java.lang.reflect.InvocationTargetException;
  24. import java.lang.reflect.Method;
  25. import java.lang.reflect.Modifier;
  26. import java.nio.charset.StandardCharsets;
  27. import java.util.ArrayList;
  28. import java.util.Collections;
  29. import java.util.List;
  30. import java.util.Map;
  31. import java.util.StringTokenizer;

  32. import org.apache.commons.logging.Log;
  33. import org.apache.commons.logging.LogFactory;
  34. import org.apache.commons.validator.util.ValidatorUtils;

  35. /**
  36.  * Contains the information to dynamically create and run a validation method. This is the class representation of a pluggable validator that can be defined in
  37.  * an xml file with the <validator> element.
  38.  *
  39.  * <strong>Note</strong>: The validation method is assumed to be thread safe.
  40.  */
  41. public class ValidatorAction implements Serializable {

  42.     private static final long serialVersionUID = 1339713700053204597L;

  43.     /**
  44.      * Logger.
  45.      */
  46.     private transient Log log = LogFactory.getLog(ValidatorAction.class);

  47.     /**
  48.      * The name of the validation.
  49.      */
  50.     private String name;

  51.     /**
  52.      * The full class name of the class containing the validation method associated with this action.
  53.      */
  54.     private String className;

  55.     /**
  56.      * The Class object loaded from the class name.
  57.      */
  58.     private Class<?> validationClass;

  59.     /**
  60.      * The full method name of the validation to be performed. The method must be thread safe.
  61.      */
  62.     private String method;

  63.     /**
  64.      * The Method object loaded from the method name.
  65.      */
  66.     private transient Method validationMethod;

  67.     /**
  68.      * <p>
  69.      * The method signature of the validation method. This should be a comma delimited list of the full class names of each parameter in the correct order that
  70.      * the method takes.
  71.      * </p>
  72.      * <p>
  73.      * Note: <code>java.lang.Object</code> is reserved for the JavaBean that is being validated. The <code>ValidatorAction</code> and <code>Field</code> that
  74.      * are associated with a field's validation will automatically be populated if they are specified in the method signature.
  75.      * </p>
  76.      */
  77.     private String methodParams = Validator.BEAN_PARAM + "," + Validator.VALIDATOR_ACTION_PARAM + "," + Validator.FIELD_PARAM;

  78.     /**
  79.      * The Class objects for each entry in methodParameterList.
  80.      */
  81.     private Class<?>[] parameterClasses;

  82.     /**
  83.      * The other <code>ValidatorAction</code>s that this one depends on. If any errors occur in an action that this one depends on, this action will not be
  84.      * processsed.
  85.      */
  86.     private String depends;

  87.     /**
  88.      * The default error message associated with this action.
  89.      */
  90.     private String msg;

  91.     /**
  92.      * An optional field to contain the name to be used if JavaScript is generated.
  93.      */
  94.     private String jsFunctionName;

  95.     /**
  96.      * An optional field to contain the class path to be used to retrieve the JavaScript function.
  97.      */
  98.     private String jsFunction;

  99.     /**
  100.      * An optional field to containing a JavaScript representation of the Java method assocated with this action.
  101.      */
  102.     private String javascript;

  103.     /**
  104.      * If the Java method matching the correct signature isn't static, the instance is stored in the action. This assumes the method is thread safe.
  105.      */
  106.     private Object instance;

  107.     /**
  108.      * An internal List representation of the other <code>ValidatorAction</code>s this one depends on (if any). This List gets updated whenever setDepends()
  109.      * gets called. This is synchronized so a call to setDepends() (which clears the List) won't interfere with a call to isDependency().
  110.      */
  111.     private final List<String> dependencyList = Collections.synchronizedList(new ArrayList<>());

  112.     /**
  113.      * An internal List representation of all the validation method's parameters defined in the methodParams String.
  114.      */
  115.     private final List<String> methodParameterList = new ArrayList<>();

  116.     /**
  117.      * Dynamically runs the validation method for this validator and returns true if the data is valid.
  118.      *
  119.      * @param field
  120.      * @param params  A Map of class names to parameter values.
  121.      * @param results
  122.      * @param pos     The index of the list property to validate if it's indexed.
  123.      * @throws ValidatorException
  124.      */
  125.     boolean executeValidationMethod(final Field field,
  126.             // TODO What is this the correct value type?
  127.             // both ValidatorAction and Validator are added as parameters
  128.             final Map<String, Object> params, final ValidatorResults results, final int pos) throws ValidatorException {

  129.         params.put(Validator.VALIDATOR_ACTION_PARAM, this);

  130.         try {
  131.             if (this.validationMethod == null) {
  132.                 synchronized (this) {
  133.                     final ClassLoader loader = this.getClassLoader(params);
  134.                     this.loadValidationClass(loader);
  135.                     this.loadParameterClasses(loader);
  136.                     this.loadValidationMethod();
  137.                 }
  138.             }

  139.             final Object[] paramValues = this.getParameterValues(params);

  140.             if (field.isIndexed()) {
  141.                 this.handleIndexedField(field, pos, paramValues);
  142.             }

  143.             Object result = null;
  144.             try {
  145.                 result = validationMethod.invoke(getValidationClassInstance(), paramValues);

  146.             } catch (IllegalArgumentException | IllegalAccessException e) {
  147.                 throw new ValidatorException(e.getMessage());
  148.             } catch (final InvocationTargetException e) {

  149.                 if (e.getTargetException() instanceof Exception) {
  150.                     throw (Exception) e.getTargetException();

  151.                 }
  152.                 if (e.getTargetException() instanceof Error) {
  153.                     throw (Error) e.getTargetException();
  154.                 }
  155.             }

  156.             final boolean valid = this.isValid(result);
  157.             if (!valid || valid && !onlyReturnErrors(params)) {
  158.                 results.add(field, this.name, valid, result);
  159.             }

  160.             if (!valid) {
  161.                 return false;
  162.             }

  163.             // TODO This catch block remains for backward compatibility. Remove
  164.             // this for Validator 2.0 when exception scheme changes.
  165.         } catch (final Exception e) {
  166.             if (e instanceof ValidatorException) {
  167.                 throw (ValidatorException) e;
  168.             }

  169.             getLog().error("Unhandled exception thrown during validation: " + e.getMessage(), e);

  170.             results.add(field, this.name, false);
  171.             return false;
  172.         }

  173.         return true;
  174.     }

  175.     /**
  176.      * @return A file name suitable for passing to a {@link ClassLoader#getResourceAsStream(String)} method.
  177.      */
  178.     private String formatJavaScriptFileName() {
  179.         String fname = this.jsFunction.substring(1);

  180.         if (!this.jsFunction.startsWith("/")) {
  181.             fname = jsFunction.replace('.', '/') + ".js";
  182.         }

  183.         return fname;
  184.     }

  185.     /**
  186.      * Used to generate the JavaScript name when it is not specified.
  187.      */
  188.     private String generateJsFunction() {
  189.         final StringBuilder jsName = new StringBuilder("org.apache.commons.validator.javascript");

  190.         jsName.append(".validate");
  191.         jsName.append(name.substring(0, 1).toUpperCase());
  192.         jsName.append(name.substring(1));

  193.         return jsName.toString();
  194.     }

  195.     /**
  196.      * Returns the ClassLoader set in the Validator contained in the parameter Map.
  197.      */
  198.     private ClassLoader getClassLoader(final Map<String, Object> params) {
  199.         final Validator v = getValidator(params);
  200.         return v.getClassLoader();
  201.     }

  202.     /**
  203.      * Gets the class of the validator action.
  204.      *
  205.      * @return Class name of the validator Action.
  206.      */
  207.     public String getClassname() {
  208.         return className;
  209.     }

  210.     /**
  211.      * Returns the dependent validator names as an unmodifiable <code>List</code>.
  212.      *
  213.      * @return List of the validator action's depedents.
  214.      */
  215.     public List<String> getDependencyList() {
  216.         return Collections.unmodifiableList(this.dependencyList);
  217.     }

  218.     /**
  219.      * Gets the dependencies of the validator action as a comma separated list of validator names.
  220.      *
  221.      * @return The validator action's dependencies.
  222.      */
  223.     public String getDepends() {
  224.         return this.depends;
  225.     }

  226.     /**
  227.      * Gets the JavaScript equivalent of the Java class and method associated with this action.
  228.      *
  229.      * @return The JavaScript validation.
  230.      */
  231.     public synchronized String getJavascript() {
  232.         return javascript;
  233.     }

  234.     /**
  235.      * Gets the JavaScript function name. This is optional and can be used instead of validator action name for the name of the JavaScript function/object.
  236.      *
  237.      * @return The JavaScript function name.
  238.      */
  239.     public String getJsFunctionName() {
  240.         return jsFunctionName;
  241.     }

  242.     /**
  243.      * Accessor method for Log instance.
  244.      *
  245.      * The Log instance variable is transient and accessing it through this method ensures it is re-initialized when this instance is de-serialized.
  246.      *
  247.      * @return The Log instance.
  248.      */
  249.     private Log getLog() {
  250.         if (log == null) {
  251.             log = LogFactory.getLog(ValidatorAction.class);
  252.         }
  253.         return log;
  254.     }

  255.     /**
  256.      * Gets the name of method being called for the validator action.
  257.      *
  258.      * @return The method name.
  259.      */
  260.     public String getMethod() {
  261.         return method;
  262.     }

  263.     /**
  264.      * Gets the method parameters for the method.
  265.      *
  266.      * @return Method's parameters.
  267.      */
  268.     public String getMethodParams() {
  269.         return methodParams;
  270.     }

  271.     /**
  272.      * Gets the message associated with the validator action.
  273.      *
  274.      * @return The message for the validator action.
  275.      */
  276.     public String getMsg() {
  277.         return msg;
  278.     }

  279.     /**
  280.      * Gets the name of the validator action.
  281.      *
  282.      * @return Validator Action name.
  283.      */
  284.     public String getName() {
  285.         return name;
  286.     }

  287.     /**
  288.      * Converts a List of parameter class names into their values contained in the parameters Map.
  289.      *
  290.      * @param params A Map of class names to parameter values.
  291.      * @return An array containing the value object for each parameter. This array is in the same order as the given List and is suitable for passing to the
  292.      *         validation method.
  293.      */
  294.     private Object[] getParameterValues(final Map<String, ? super Object> params) {

  295.         final Object[] paramValue = new Object[this.methodParameterList.size()];

  296.         for (int i = 0; i < this.methodParameterList.size(); i++) {
  297.             final String paramClassName = this.methodParameterList.get(i);
  298.             paramValue[i] = params.get(paramClassName);
  299.         }

  300.         return paramValue;
  301.     }

  302.     /**
  303.      * Gets an instance of the validation class or null if the validation method is static so does not require an instance to be executed.
  304.      */
  305.     private Object getValidationClassInstance() throws ValidatorException {
  306.         if (Modifier.isStatic(this.validationMethod.getModifiers())) {
  307.             this.instance = null;

  308.         } else if (this.instance == null) {
  309.             try {
  310.                 this.instance = this.validationClass.getConstructor().newInstance();
  311.             } catch (final ReflectiveOperationException e) {
  312.                 final String msg1 = "Couldn't create instance of " + this.className + ".  " + e.getMessage();

  313.                 throw new ValidatorException(msg1);
  314.             }
  315.         }

  316.         return this.instance;
  317.     }

  318.     private Validator getValidator(final Map<String, Object> params) {
  319.         return (Validator) params.get(Validator.VALIDATOR_PARAM);
  320.     }

  321.     /**
  322.      * Modifies the paramValue array with indexed fields.
  323.      *
  324.      * @param field
  325.      * @param pos
  326.      * @param paramValues
  327.      */
  328.     private void handleIndexedField(final Field field, final int pos, final Object[] paramValues) throws ValidatorException {

  329.         final int beanIndex = this.methodParameterList.indexOf(Validator.BEAN_PARAM);
  330.         final int fieldIndex = this.methodParameterList.indexOf(Validator.FIELD_PARAM);

  331.         final Object[] indexedList = field.getIndexedProperty(paramValues[beanIndex]);

  332.         // Set current iteration object to the parameter array
  333.         paramValues[beanIndex] = indexedList[pos];

  334.         // Set field clone with the key modified to represent
  335.         // the current field
  336.         final Field indexedField = (Field) field.clone();
  337.         indexedField.setKey(ValidatorUtils.replace(indexedField.getKey(), Field.TOKEN_INDEXED, "[" + pos + "]"));

  338.         paramValues[fieldIndex] = indexedField;
  339.     }

  340.     /**
  341.      * Initialize based on set.
  342.      */
  343.     protected void init() {
  344.         this.loadJavascriptFunction();
  345.     }

  346.     /**
  347.      * Checks whether or not the value passed in is in the depends field.
  348.      *
  349.      * @param validatorName Name of the dependency to check.
  350.      * @return Whether the named validator is a dependant.
  351.      */
  352.     public boolean isDependency(final String validatorName) {
  353.         return this.dependencyList.contains(validatorName);
  354.     }

  355.     /**
  356.      * If the result object is a <code>Boolean</code>, it will return its value. If not it will return {@code false} if the object is {@code null} and
  357.      * {@code true} if it isn't.
  358.      */
  359.     private boolean isValid(final Object result) {
  360.         if (result instanceof Boolean) {
  361.             final Boolean valid = (Boolean) result;
  362.             return valid.booleanValue();
  363.         }
  364.         return result != null;
  365.     }

  366.     /**
  367.      * @return true if the JavaScript for this action has already been loaded.
  368.      */
  369.     private boolean javaScriptAlreadyLoaded() {
  370.         return this.javascript != null;
  371.     }

  372.     /**
  373.      * Load the JavaScript function specified by the given path. For this implementation, the <code>jsFunction</code> property should contain a fully qualified
  374.      * package and script name, separated by periods, to be loaded from the class loader that created this instance.
  375.      *
  376.      * TODO if the path begins with a '/' the path will be intepreted as absolute, and remain unchanged. If this fails then it will attempt to treat the path as
  377.      * a file path. It is assumed the script ends with a '.js'.
  378.      */
  379.     protected synchronized void loadJavascriptFunction() {

  380.         if (this.javaScriptAlreadyLoaded()) {
  381.             return;
  382.         }

  383.         if (getLog().isTraceEnabled()) {
  384.             getLog().trace("  Loading function begun");
  385.         }

  386.         if (this.jsFunction == null) {
  387.             this.jsFunction = this.generateJsFunction();
  388.         }

  389.         final String javaScriptFileName = this.formatJavaScriptFileName();

  390.         if (getLog().isTraceEnabled()) {
  391.             getLog().trace("  Loading js function '" + javaScriptFileName + "'");
  392.         }

  393.         this.javascript = this.readJavaScriptFile(javaScriptFileName);

  394.         if (getLog().isTraceEnabled()) {
  395.             getLog().trace("  Loading JavaScript function completed");
  396.         }

  397.     }

  398.     /**
  399.      * Converts a List of parameter class names into their Class objects. Stores the output in {@link #parameterClasses}. This array is in the same order as the
  400.      * given List and is suitable for passing to the validation method.
  401.      *
  402.      * @throws ValidatorException if a class cannot be loaded.
  403.      */
  404.     private void loadParameterClasses(final ClassLoader loader) throws ValidatorException {

  405.         if (this.parameterClasses != null) {
  406.             return;
  407.         }

  408.         final Class<?>[] parameterClasses = new Class[this.methodParameterList.size()];

  409.         for (int i = 0; i < this.methodParameterList.size(); i++) {
  410.             final String paramClassName = this.methodParameterList.get(i);

  411.             try {
  412.                 parameterClasses[i] = loader.loadClass(paramClassName);

  413.             } catch (final ClassNotFoundException e) {
  414.                 throw new ValidatorException(e.getMessage());
  415.             }
  416.         }

  417.         this.parameterClasses = parameterClasses;
  418.     }

  419.     /**
  420.      * Load the Class object for the configured validation class name.
  421.      *
  422.      * @param loader The ClassLoader used to load the Class object.
  423.      * @throws ValidatorException
  424.      */
  425.     private void loadValidationClass(final ClassLoader loader) throws ValidatorException {

  426.         if (this.validationClass != null) {
  427.             return;
  428.         }

  429.         try {
  430.             this.validationClass = loader.loadClass(this.className);
  431.         } catch (final ClassNotFoundException e) {
  432.             throw new ValidatorException(e.toString());
  433.         }
  434.     }

  435.     /**
  436.      * Load the Method object for the configured validation method name.
  437.      *
  438.      * @throws ValidatorException
  439.      */
  440.     private void loadValidationMethod() throws ValidatorException {
  441.         if (this.validationMethod != null) {
  442.             return;
  443.         }

  444.         try {
  445.             this.validationMethod = this.validationClass.getMethod(this.method, this.parameterClasses);

  446.         } catch (final NoSuchMethodException e) {
  447.             throw new ValidatorException("No such validation method: " + e.getMessage());
  448.         }
  449.     }

  450.     /**
  451.      * Returns the onlyReturnErrors setting in the Validator contained in the parameter Map.
  452.      */
  453.     private boolean onlyReturnErrors(final Map<String, Object> params) {
  454.         final Validator v = getValidator(params);
  455.         return v.getOnlyReturnErrors();
  456.     }

  457.     /**
  458.      * Opens an input stream for reading the specified resource.
  459.      * <p>
  460.      * The search order is described in the documentation for {@link ClassLoader#getResource(String)}.
  461.      * </p>
  462.      *
  463.      * @param name The resource name
  464.      * @return An input stream for reading the resource, or {@code null} if the resource could not be found
  465.      */
  466.     private InputStream openInputStream(final String javaScriptFileName, final ClassLoader classLoader) {
  467.         InputStream is = null;
  468.         if (classLoader != null) {
  469.             is = classLoader.getResourceAsStream(javaScriptFileName);
  470.         }
  471.         if (is == null) {
  472.             return getClass().getResourceAsStream(javaScriptFileName);
  473.         }
  474.         return is;
  475.     }

  476.     /**
  477.      * Reads a JavaScript function from a file.
  478.      *
  479.      * @param javaScriptFileName The file containing the JavaScript.
  480.      * @return The JavaScript function or null if it could not be loaded.
  481.      */
  482.     private String readJavaScriptFile(final String javaScriptFileName) {
  483.         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
  484.         if (classLoader == null) {
  485.             classLoader = getClass().getClassLoader();
  486.         }
  487.         // BufferedReader closes InputStreamReader closes InputStream
  488.         final InputStream is = openInputStream(javaScriptFileName, classLoader);
  489.         if (is == null) {
  490.             getLog().debug("  Unable to read javascript name " + javaScriptFileName);
  491.             return null;
  492.         }
  493.         final StringBuilder buffer = new StringBuilder();
  494.         // TODO encoding
  495.         try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
  496.             String line = null;
  497.             while ((line = reader.readLine()) != null) {
  498.                 buffer.append(line).append("\n");
  499.             }
  500.         } catch (final IOException e) {
  501.             getLog().error("Error reading JavaScript file.", e);

  502.         }
  503.         final String function = buffer.toString();
  504.         return function.isEmpty() ? null : function;
  505.     }

  506.     /**
  507.      * Sets the class of the validator action.
  508.      *
  509.      * @param className Class name of the validator Action.
  510.      * @deprecated Use {@link #setClassName(String)}.
  511.      */
  512.     @Deprecated
  513.     public void setClassname(final String className) {
  514.         this.className = className;
  515.     }

  516.     /**
  517.      * Sets the class of the validator action.
  518.      *
  519.      * @param className Class name of the validator Action.
  520.      */
  521.     public void setClassName(final String className) {
  522.         this.className = className;
  523.     }

  524.     /**
  525.      * Sets the dependencies of the validator action.
  526.      *
  527.      * @param depends A comma separated list of validator names.
  528.      */
  529.     public void setDepends(final String depends) {
  530.         this.depends = depends;

  531.         this.dependencyList.clear();

  532.         final StringTokenizer st = new StringTokenizer(depends, ",");
  533.         while (st.hasMoreTokens()) {
  534.             final String depend = st.nextToken().trim();

  535.             if (depend != null && !depend.isEmpty()) {
  536.                 this.dependencyList.add(depend);
  537.             }
  538.         }
  539.     }

  540.     /**
  541.      * Sets the JavaScript equivalent of the Java class and method associated with this action.
  542.      *
  543.      * @param javaScript The JavaScript validation.
  544.      */
  545.     public synchronized void setJavascript(final String javaScript) {
  546.         if (jsFunction != null) {
  547.             throw new IllegalStateException("Cannot call setJavascript() after calling setJsFunction()");
  548.         }

  549.         this.javascript = javaScript;
  550.     }

  551.     /**
  552.      * Sets the fully qualified class path of the JavaScript function.
  553.      * <p>
  554.      * This is optional and can be used <strong>instead</strong> of the setJavascript(). Attempting to call both <code>setJsFunction</code> and
  555.      * <code>setJavascript</code> will result in an <code>IllegalStateException</code> being thrown.
  556.      * </p>
  557.      * <p>
  558.      * If <strong>neither</strong> setJsFunction or setJavascript is set then validator will attempt to load the default JavaScript definition.
  559.      * </p>
  560.      *
  561.      * <pre>
  562.      * <b>Examples</b>
  563.      *   If in the validator.xml :
  564.      * #1:
  565.      *      &lt;validator name="tire"
  566.      *            jsFunction="com.yourcompany.project.tireFuncion"&gt;
  567.      *     Validator will attempt to load com.yourcompany.project.validateTireFunction.js from
  568.      *     its class path.
  569.      * #2:
  570.      *    &lt;validator name="tire"&gt;
  571.      *      Validator will use the name attribute to try and load
  572.      *         org.apache.commons.validator.javascript.validateTire.js
  573.      *      which is the default JavaScript definition.
  574.      * </pre>
  575.      *
  576.      * @param jsFunction The JavaScript function's fully qualified class path.
  577.      */
  578.     public synchronized void setJsFunction(final String jsFunction) {
  579.         if (javascript != null) {
  580.             throw new IllegalStateException("Cannot call setJsFunction() after calling setJavascript()");
  581.         }

  582.         this.jsFunction = jsFunction;
  583.     }

  584.     /**
  585.      * Sets the JavaScript function name. This is optional and can be used instead of validator action name for the name of the JavaScript function/object.
  586.      *
  587.      * @param jsFunctionName The JavaScript function name.
  588.      */
  589.     public void setJsFunctionName(final String jsFunctionName) {
  590.         this.jsFunctionName = jsFunctionName;
  591.     }

  592.     /**
  593.      * Sets the name of method being called for the validator action.
  594.      *
  595.      * @param method The method name.
  596.      */
  597.     public void setMethod(final String method) {
  598.         this.method = method;
  599.     }

  600.     /**
  601.      * Sets the method parameters for the method.
  602.      *
  603.      * @param methodParams A comma separated list of parameters.
  604.      */
  605.     public void setMethodParams(final String methodParams) {
  606.         this.methodParams = methodParams;

  607.         this.methodParameterList.clear();

  608.         final StringTokenizer st = new StringTokenizer(methodParams, ",");
  609.         while (st.hasMoreTokens()) {
  610.             final String value = st.nextToken().trim();

  611.             if (value != null && !value.isEmpty()) {
  612.                 this.methodParameterList.add(value);
  613.             }
  614.         }
  615.     }

  616.     /**
  617.      * Sets the message associated with the validator action.
  618.      *
  619.      * @param msg The message for the validator action.
  620.      */
  621.     public void setMsg(final String msg) {
  622.         this.msg = msg;
  623.     }

  624.     /**
  625.      * Sets the name of the validator action.
  626.      *
  627.      * @param name Validator Action name.
  628.      */
  629.     public void setName(final String name) {
  630.         this.name = name;
  631.     }

  632.     /**
  633.      * Returns a string representation of the object.
  634.      *
  635.      * @return a string representation.
  636.      */
  637.     @Override
  638.     public String toString() {
  639.         final StringBuilder results = new StringBuilder("ValidatorAction: ");
  640.         results.append(name);
  641.         results.append("\n");

  642.         return results.toString();
  643.     }
  644. }