0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017: package org.apache.cocoon.acting;
0018:
0019: import org.apache.avalon.framework.configuration.Configuration;
0020: import org.apache.avalon.framework.configuration.ConfigurationException;
0021: import org.apache.avalon.framework.parameters.Parameters;
0022:
0023: import org.apache.cocoon.Constants;
0024: import org.apache.cocoon.ProcessingException;
0025: import org.apache.cocoon.environment.SourceResolver;
0026: import org.apache.cocoon.environment.ObjectModelHelper;
0027: import org.apache.cocoon.environment.Redirector;
0028: import org.apache.cocoon.sitemap.SitemapParameters;
0029: import org.apache.commons.lang.BooleanUtils;
0030: import org.apache.commons.lang.StringUtils;
0031:
0032: import org.apache.regexp.RE;
0033: import org.apache.regexp.RESyntaxException;
0034:
0035: import java.util.Collection;
0036: import java.util.HashMap;
0037: import java.util.HashSet;
0038: import java.util.Iterator;
0039: import java.util.Map;
0040: import java.util.Set;
0041: import java.util.Vector;
0042:
0043: /**
0044: * Abstract implementation of action that needs to perform validation of
0045: * parameters (from session, from request, etc.). All `validator' actions
0046: * share the same description xml file. In such file every parameter is
0047: * described via its name, type and its constraints. One large description
0048: * file can be used among all validator actions, because each action should
0049: * explicitely specify which parameters to validate - through a sitemap
0050: * parameter.
0051: *
0052: * <h3>Variant 1</h3>
0053: * <pre>
0054: * <map:act type="validator">
0055: * <parameter name="descriptor" value="context://descriptor.xml">
0056: * <parameter name="validate" value="username,password">
0057: * </map:act>
0058: * </pre>
0059: *
0060: * <p>The list of parameters to be validated is specified as a comma
0061: * separated list of their names. descriptor.xml can therefore be used
0062: * among many various actions. If the list contains only of <code>*</code>,
0063: * all parameters in the file will be validated.</p>
0064: *
0065: * <h3>Variant 2</h3>
0066: * <pre>
0067: * <map:act type="validator">
0068: * <parameter name="descriptor" value="context://descriptor.xml">
0069: * <parameter name="constraint-set" value="is-logged-in">
0070: * </map:act>
0071: * </pre>
0072: *
0073: * <p>The parameter "constraint-set" tells to take a given
0074: * "constraint-set" from description file and test all parameters
0075: * against given criteria. This variant is more powerful, more aspect
0076: * oriented and more flexibile than the previous one, because it
0077: * allows comparsion constructs, etc. See AbstractValidatorAction
0078: * documentation.</p>
0079: *
0080: * <p>For even more powerful validation, constraints can be grouped
0081: * and used independently of the parameter name. If a validate element
0082: * has a <code>rule</code> attribute, it uses the parameter with that
0083: * name as a rule template and validates the parameter from the
0084: * <code>name</code> attribute with that rule.</p>
0085: *
0086: * <p>This action returns null when validation fails, otherwise it
0087: * provides all validated parameters to the sitemap via {name}
0088: * expression.</p>
0089: *
0090: * <p>In addition a request attribute
0091: * <code>org.apache.cocoon.acting.FormValidatorAction.results</code>
0092: * contains the validation results in both cases and make it available
0093: * to XSPs. The special parameter "*" contains either the validation
0094: * result "OK", if all parameters were validated successfully, or
0095: * "ERROR" otherwise. Mind you that redirections create new request
0096: * objects and thus the result is not available for the target
0097: * page.</p>
0098: *
0099: * <pre>
0100: * <root>
0101: * <parameter name="username" type="string" nullable="no"/>
0102: * <parameter name="role" type="string" nullable="no"/>
0103: * <parameter name="oldpassword" type="string" nullable="no"/>
0104: * <parameter name="newpassword" type="string" nullable="no"/>
0105: * <parameter name="renewpassword" type="string" nullable="no"/>
0106: * <parameter name="id" type="long" nullable="no"/>
0107: * <parameter name="sallary" type="double" nullable="no"/>
0108: * <parameter name="theme" type="string" nullable="yes" default="dflt"/>
0109: * <constraint-set name="is-logged-in">
0110: * <validate name="username"/>
0111: * <validate name="role"/>
0112: * </constraint-set>
0113: *
0114: * <constraint-set name="is-in-admin-role">
0115: * <validate name="username"/>
0116: * <validate name="role" equals-to="admin"/>
0117: * </constraint-set>
0118: *
0119: * <constraint-set name="new-passwords-match">
0120: * <validate name="oldpassword"/>
0121: * <validate name="newpassword"/>
0122: * <validate name="renewpassword"
0123: * equals-to-param="newpass"/>
0124: * </constraint-set>
0125: *
0126: * <constraint-set name="all">
0127: * <include name="is-logged-in"/>
0128: * <include name="is-in-admin-role"/>
0129: * <include name="new-passwords-match"/>
0130: * </constraint-set>
0131: * </root>
0132: * </pre>
0133: *
0134: * <h3>The types recognized by validator and their attributes</h3>
0135: * <table border="1">
0136: * <tr>
0137: * <td><b>string</b></td><td>nullable="yes|no" default="str"</td>
0138: * </tr>
0139: * <tr>
0140: * <td><b>long</b></td><td>nullable="yes|no" default="123123"</td>
0141: * </tr>
0142: * <tr>
0143: * <td><b>double</b></td><td>nullable="yes|no" default="0.5"</td>
0144: * </tr>
0145: * </table>
0146: *
0147: * <p>Default value takes place only when specified parameter is
0148: * nullable and really is null or empty. Long numbers may be specified
0149: * in decimal, hex or octal values as accepted by java.Lang.decode
0150: * (String s).</p>
0151: *
0152: * <h3>Constraints</h3>
0153: * <table border="1">
0154: * <tr>
0155: * <td>matches-regex</td><td>POSIX regular expression</td>
0156: * </tr>
0157: * <tr>
0158: * <td>min-len</td><td>positive integer</td>
0159: * </tr>
0160: * <tr>
0161: * <td>max-len</td><td>positive integer</td>
0162: * </tr>
0163: * <tr>
0164: * <td>min</td><td>Double / Long</td>
0165: * </tr>
0166: * <tr>
0167: * <td>max</td><td>Double / Long</td>
0168: * </tr>
0169: * </table>
0170: *
0171: * <p>Constraints can be defined globally for a parameter and can be
0172: * overridden by redefinition in a constraint-set. Thus if e.g. a
0173: * database field can take at maximum 200 character, this property can
0174: * be set globally.</p>
0175: *
0176: * <p>Values in parameter arrays are validated individually and the
0177: * worst error is reported back.</p>
0178: *
0179: * <h3>The attributes recognized in "constraint-set"</h3>
0180: * <table>
0181: * <tr>
0182: * <td>equals-to-param</td><td>parameter name</td>
0183: * </tr>
0184: * <tr>
0185: * <td>equals-to</td><td>string constant</td>
0186: * </tr>
0187: * </table>
0188: * @author <a href="mailto:Martin.Man@seznam.cz">Martin Man</a>
0189: * @author <a href="mailto:haul@apache.org">Christian Haul</a>
0190: * @version CVS $Id: AbstractValidatorAction.java 433543 2006-08-22 06:22:54Z crossley $
0191: */
0192: public abstract class AbstractValidatorAction extends
0193: AbstractComplementaryConfigurableAction {
0194:
0195: /**
0196: * Reads parameter values for all parameters that are contained in the active
0197: * constraint list. If a parameter has multiple values, all are stored in the
0198: * resulting map.
0199: *
0200: * @param objectModel the object model
0201: * @param set a collection of parameter names
0202: * @return HashMap
0203: */
0204: abstract protected HashMap createMapOfParameters(Map objectModel,
0205: Collection set);
0206:
0207: /**
0208: * Are parameters encoded as strings?
0209: * @return boolean
0210: */
0211: abstract boolean isStringEncoded();
0212:
0213: /*
0214: * main method
0215: */
0216: public Map act(Redirector redirector, SourceResolver resolver,
0217: Map objectModel, String src, Parameters parameters)
0218: throws Exception {
0219:
0220: Configuration conf = this .getDescriptor(resolver, objectModel,
0221: parameters);
0222: if (conf == null)
0223: return null;
0224:
0225: String valStr = parameters.getParameter("validate",
0226: (String) settings.get("validate", "")).trim();
0227: String valSetStr = parameters.getParameter("validate-set",
0228: (String) settings.get("validate-set", "")).trim();
0229: String constraintSetStr = parameters.getParameter(
0230: "constraint-set",
0231: (String) settings.get("constraint-set", "")).trim();
0232: if (!"".equals(valSetStr)) {
0233: if (getLogger().isInfoEnabled()) {
0234: getLogger()
0235: .info(
0236: "Using sitemap parameter 'validate-set' for specifying "
0237: + "the constraint-set for the ValidatorActions is deprecated in "
0238: + "favor of 'constraint-set' due to consistency in the naming.");
0239: }
0240: if ("".equals(constraintSetStr)) {
0241: constraintSetStr = valSetStr;
0242: }
0243: }
0244: Map desc = this .indexConfiguration(conf
0245: .getChildren("parameter"));
0246:
0247: Map actionMap = new HashMap();
0248: Map resultMap = new HashMap();
0249: Collection params = null;
0250: boolean allOK = false;
0251:
0252: if (!"".equals(valStr)) {
0253: if (getLogger().isDebugEnabled()) {
0254: getLogger()
0255: .debug(
0256: "Validating parameters as specified via 'validate' parameter");
0257: }
0258: params = this .getSetOfParameterNamesFromSitemap(valStr,
0259: desc);
0260: } else if (!"".equals(constraintSetStr)) {
0261: if (getLogger().isDebugEnabled()) {
0262: getLogger().debug(
0263: "Validating parameters from given constraint-set "
0264: + constraintSetStr);
0265: }
0266: Map csets = this .indexConfiguration(conf
0267: .getChildren("constraint-set"));
0268: params = this .resolveConstraints(constraintSetStr, csets);
0269: }
0270:
0271: if (params == null) {
0272: throw new ProcessingException(
0273: "Neither a constraint-set nor parameters in the sitemap "
0274: + "were specified for validating at "
0275: + SitemapParameters
0276: .getStatementLocation(parameters));
0277: }
0278: HashMap values = this
0279: .createMapOfParameters(objectModel, params);
0280: allOK = this .validateSetOfParameters(desc, actionMap,
0281: resultMap, params, values, this .isStringEncoded());
0282:
0283: return this .setResult(objectModel, actionMap, resultMap, allOK);
0284: }
0285:
0286: /**
0287: * Try to validate given parameter.
0288: * @param name The name of the parameter to validate.
0289: * @param constraints Configuration of all constraints for this
0290: * parameter as taken from the description XML file.
0291: * @param conf Configuration of all parameters as taken from the
0292: * description XML file.
0293: * @param params The map of parameters.
0294: * @param isString Indicates wheter given param to validate is
0295: * string (as taken from HTTP request for example) or wheteher it
0296: * should be regular instance of java.lang.Double, java.lang.Long,
0297: * etc.
0298: * @return The validated parameter.
0299: */
0300: public ValidatorActionHelper validateParameter(String name,
0301: Configuration constraints, Map conf, Map params,
0302: boolean isString) {
0303:
0304: return validateParameter(name, name, constraints, conf, params,
0305: isString);
0306: }
0307:
0308: /**
0309: * Try to validate given parameter.
0310: * @param name The actual name of the parameter to validate.
0311: * @param rule The name of the parameter element that contains the
0312: * rule that should be used for validation.
0313: * @param constraints Configuration of all constraints for this
0314: * parameter as taken from the description XML file.
0315: * @param conf Configuration of all parameters as taken from the
0316: * description XML file.
0317: * @param params The map of parameters.
0318: * @param isString Indicates wheter given param to validate is
0319: * string (as taken from HTTP request for example) or wheteher it
0320: * should be regular instance of java.lang.Double, java.lang.Long,
0321: * etc.
0322: * @return The validated parameter.
0323: */
0324: public ValidatorActionHelper validateParameter(String name,
0325: String rule, Configuration constraints, Map conf,
0326: Map params, boolean isString) {
0327: String type = null;
0328:
0329: if (getLogger().isDebugEnabled())
0330: getLogger().debug(
0331: "Validating parameter: " + name + " using rule: "
0332: + rule);
0333:
0334: /* try to find matching param description in conf tree */
0335: try {
0336: Configuration theConf = (Configuration) conf.get(rule);
0337: type = theConf.getAttribute("type");
0338:
0339: return validateValue(name, constraints, theConf, params,
0340: isString, type);
0341:
0342: } catch (Exception e) {
0343: if (getLogger().isDebugEnabled())
0344: getLogger().debug(
0345: "No type specified for parameter " + name);
0346: return null;
0347: }
0348: }
0349:
0350: /**
0351: * Validate a single parameter value.
0352: *
0353: * @param name String holding the name of the parameter
0354: * @param constraints Configuration holding the constraint set configuration for the parameter
0355: * @param conf Configuration holding the parameter configuration
0356: * @param params Map of parameter values to be validated
0357: * @param isString boolean indicating if the value is string encoded
0358: * @param type string holding the name of the datatype to validate value
0359: * @return ValidatorActionHelper
0360: */
0361: protected ValidatorActionHelper validateValue(String name,
0362: Configuration constraints, Configuration conf, Map params,
0363: boolean isString, String type) {
0364: Object value = params.get(name);
0365:
0366: if (value != null && value.getClass().isArray()) {
0367: Object[] values = (Object[]) value;
0368: ValidatorActionHelper vaH = null;
0369: ValidatorActionResult vaR = ValidatorActionResult.OK;
0370: for (int j = 0; j < values.length; j++) {
0371: value = values[j];
0372: if ("string".equals(type)) {
0373: vaH = validateString(name, constraints, conf,
0374: params, value);
0375: } else if ("long".equals(type)) {
0376: vaH = validateLong(name, constraints, conf, params,
0377: isString, value);
0378: } else if ("double".equals(type)) {
0379: vaH = validateDouble(name, constraints, conf,
0380: params, isString, value);
0381: } else {
0382: if (getLogger().isDebugEnabled())
0383: getLogger().debug(
0384: "Unknown type " + type
0385: + " specified for parameter "
0386: + name);
0387: return null;
0388: }
0389: vaR = (vaR.getPos() < vaH.getResult().getPos() ? vaH
0390: .getResult() : vaR);
0391: }
0392: return new ValidatorActionHelper(vaH.getObject(), vaR);
0393: } else {
0394: if ("string".equals(type)) {
0395: return validateString(name, constraints, conf, params,
0396: value);
0397: } else if ("long".equals(type)) {
0398: return validateLong(name, constraints, conf, params,
0399: isString, value);
0400: } else if ("double".equals(type)) {
0401: return validateDouble(name, constraints, conf, params,
0402: isString, value);
0403: } else {
0404: if (getLogger().isDebugEnabled())
0405: getLogger().debug(
0406: "Unknown type " + type
0407: + " specified for parameter "
0408: + name);
0409: }
0410: return null;
0411: }
0412: }
0413:
0414: /**
0415: * Validates nullability and default value for given parameter. If given
0416: * constraints are not null they are validated as well.
0417: */
0418: private ValidatorActionHelper validateString(String name,
0419: Configuration constraints, Configuration conf, Map params,
0420: Object param) {
0421:
0422: String value = null;
0423: String dflt = getDefault(conf, constraints);
0424: boolean nullable = getNullable(conf, constraints);
0425:
0426: if (getLogger().isDebugEnabled())
0427: getLogger().debug("Validating string parameter " + name);
0428: try {
0429: value = getStringValue(param);
0430: } catch (Exception e) {
0431: // ClassCastException
0432: return new ValidatorActionHelper(value,
0433: ValidatorActionResult.ERROR);
0434: }
0435: if (value == null) {
0436: if (getLogger().isDebugEnabled())
0437: getLogger().debug(
0438: "String parameter " + name + " is null");
0439: if (!nullable) {
0440: return new ValidatorActionHelper(value,
0441: ValidatorActionResult.ISNULL);
0442: } else {
0443: return new ValidatorActionHelper(dflt);
0444: }
0445: }
0446: if (constraints != null) {
0447: String eq = constraints.getAttribute("equals-to", "");
0448: eq = conf.getAttribute("equals-to", eq);
0449:
0450: String eqp = constraints
0451: .getAttribute("equals-to-param", "");
0452: eqp = conf.getAttribute("equals-to-param", eqp);
0453:
0454: String regex = conf.getAttribute("matches-regex", "");
0455: regex = constraints.getAttribute("matches-regex", regex);
0456:
0457: String oneOf = conf.getAttribute("one-of", "");
0458: oneOf = constraints.getAttribute("one-of", oneOf);
0459:
0460: Long minlen = getAttributeAsLong(conf, "min-len", null);
0461: minlen = getAttributeAsLong(constraints, "min-len", minlen);
0462:
0463: Long maxlen = getAttributeAsLong(conf, "max-len", null);
0464: maxlen = getAttributeAsLong(constraints, "max-len", maxlen);
0465:
0466: // Validate whether param is equal to constant
0467: if (!"".equals(eq)) {
0468: if (getLogger().isDebugEnabled())
0469: getLogger().debug(
0470: "String parameter " + name
0471: + " should be equal to " + eq);
0472: if (!value.equals(eq)) {
0473: if (getLogger().isDebugEnabled())
0474: getLogger().debug("and it is not");
0475: return new ValidatorActionHelper(value,
0476: ValidatorActionResult.NOMATCH);
0477: }
0478: }
0479:
0480: // Validate whether param is equal to another param
0481: // FIXME: take default value of param being compared with into
0482: // account?
0483: if (!"".equals(eqp)) {
0484: if (getLogger().isDebugEnabled())
0485: getLogger().debug(
0486: "String parameter " + name
0487: + " should be equal to "
0488: + params.get(eqp));
0489: if (!value.equals(params.get(eqp))) {
0490: if (getLogger().isDebugEnabled())
0491: getLogger().debug("and it is not");
0492: return new ValidatorActionHelper(value,
0493: ValidatorActionResult.NOMATCH);
0494: }
0495: }
0496:
0497: // Validate whether param length is at least of minimum length
0498: if (minlen != null) {
0499: if (getLogger().isDebugEnabled())
0500: getLogger().debug(
0501: "String parameter " + name
0502: + " should be at least " + minlen
0503: + " characters long");
0504: if (value.length() < minlen.longValue()) {
0505: if (getLogger().isDebugEnabled())
0506: getLogger().debug(
0507: "and it is shorter (" + value.length()
0508: + ")");
0509: return new ValidatorActionHelper(value,
0510: ValidatorActionResult.TOOSMALL);
0511: }
0512: }
0513:
0514: // Validate whether param length is at most of maximum length
0515: if (maxlen != null) {
0516: if (getLogger().isDebugEnabled())
0517: getLogger().debug(
0518: "String parameter " + name
0519: + " should be at most " + maxlen
0520: + " characters long");
0521:
0522: if (value.length() > maxlen.longValue()) {
0523: if (getLogger().isDebugEnabled())
0524: getLogger().debug(
0525: "and it is longer (" + value.length()
0526: + ")");
0527: return new ValidatorActionHelper(value,
0528: ValidatorActionResult.TOOLARGE);
0529: }
0530: }
0531:
0532: // Validate wheter param matches regular expression
0533: if (!"".equals(regex)) {
0534: if (getLogger().isDebugEnabled())
0535: getLogger().debug(
0536: "String parameter " + name
0537: + " should match regexp \"" + regex
0538: + "\"");
0539: try {
0540: RE r = new RE(regex);
0541: if (!r.match(value)) {
0542: if (getLogger().isDebugEnabled())
0543: getLogger().debug("and it does not match");
0544: return new ValidatorActionHelper(value,
0545: ValidatorActionResult.NOMATCH);
0546: }
0547: } catch (RESyntaxException rese) {
0548: if (getLogger().isDebugEnabled())
0549: getLogger().error(
0550: "String parameter " + name
0551: + " regex error ", rese);
0552: return new ValidatorActionHelper(value,
0553: ValidatorActionResult.NOMATCH);
0554: }
0555: }
0556:
0557: // Validates against a set of possibilities
0558: if (!"".equals(oneOf)) {
0559: if (getLogger().isDebugEnabled())
0560: getLogger().debug(
0561: "String parameter " + name
0562: + " should be one of \"" + oneOf
0563: + "\"");
0564: if (!oneOf.startsWith("|"))
0565: oneOf = "|" + oneOf;
0566: if (!oneOf.endsWith("|"))
0567: oneOf = oneOf + "|";
0568: if (value.indexOf("|") != -1) {
0569: if (getLogger().isDebugEnabled())
0570: getLogger()
0571: .debug(
0572: "String parameter "
0573: + name
0574: + "contains \"|\" - can't validate that.");
0575: return new ValidatorActionHelper(value,
0576: ValidatorActionResult.ERROR);
0577: }
0578: if (oneOf.indexOf("|" + value + "|") == -1) {
0579: if (getLogger().isDebugEnabled())
0580: getLogger().debug("and it is not");
0581: return new ValidatorActionHelper(value,
0582: ValidatorActionResult.NOMATCH);
0583: }
0584: return new ValidatorActionHelper(value,
0585: ValidatorActionResult.OK);
0586:
0587: }
0588:
0589: }
0590: return new ValidatorActionHelper(value);
0591: }
0592:
0593: /**
0594: * Validates nullability and default value for given parameter. If given
0595: * constraints are not null they are validated as well.
0596: */
0597: private ValidatorActionHelper validateLong(String name,
0598: Configuration constraints, Configuration conf, Map params,
0599: boolean is_string, Object param) {
0600:
0601: boolean nullable = getNullable(conf, constraints);
0602: Long value = null;
0603: Long dflt = getLongValue(getDefault(conf, constraints), true);
0604:
0605: if (getLogger().isDebugEnabled())
0606: getLogger().debug(
0607: "Validating long parameter " + name
0608: + " (encoded in a string: " + is_string
0609: + ")");
0610: try {
0611: value = getLongValue(param, is_string);
0612: } catch (Exception e) {
0613: // Unable to parse long
0614: return new ValidatorActionHelper(value,
0615: ValidatorActionResult.ERROR);
0616: }
0617: if (value == null) {
0618: if (getLogger().isDebugEnabled())
0619: getLogger()
0620: .debug("Long parameter " + name + " is null");
0621: if (!nullable) {
0622: return new ValidatorActionHelper(value,
0623: ValidatorActionResult.ISNULL);
0624: } else {
0625: return new ValidatorActionHelper(dflt);
0626: }
0627: }
0628: if (constraints != null) {
0629: Long eq = getAttributeAsLong(constraints, "equals-to", null);
0630: String eqp = constraints
0631: .getAttribute("equals-to-param", "");
0632:
0633: Long min = getAttributeAsLong(conf, "min", null);
0634: min = getAttributeAsLong(constraints, "min", min);
0635:
0636: Long max = getAttributeAsLong(conf, "max", null);
0637: max = getAttributeAsLong(constraints, "max", max);
0638:
0639: // Validate whether param is equal to constant
0640: if (eq != null) {
0641: if (getLogger().isDebugEnabled())
0642: getLogger().debug(
0643: "Long parameter " + name
0644: + " should be equal to " + eq);
0645:
0646: if (!value.equals(eq)) {
0647: if (getLogger().isDebugEnabled())
0648: getLogger().debug("and it is not");
0649: return new ValidatorActionHelper(value,
0650: ValidatorActionResult.NOMATCH);
0651: }
0652: }
0653:
0654: // Validate whether param is equal to another param
0655: // FIXME: take default value of param being compared with into
0656: // account?
0657: if (!"".equals(eqp)) {
0658: if (getLogger().isDebugEnabled())
0659: getLogger().debug(
0660: "Long parameter " + name
0661: + " should be equal to "
0662: + params.get(eqp));
0663: // Request parameter is stored as string.
0664: // Need to convert it beforehand.
0665: try {
0666: Long _eqp = new Long(Long.parseLong((String) params
0667: .get(eqp)));
0668: if (!value.equals(_eqp)) {
0669: if (getLogger().isDebugEnabled())
0670: getLogger().debug("and it is not");
0671: return new ValidatorActionHelper(value,
0672: ValidatorActionResult.NOMATCH);
0673: }
0674: } catch (NumberFormatException nfe) {
0675: if (getLogger().isDebugEnabled())
0676: getLogger().debug(
0677: "Long parameter " + name + ": " + eqp
0678: + " is no long", nfe);
0679: return new ValidatorActionHelper(value,
0680: ValidatorActionResult.NOMATCH);
0681: }
0682: }
0683:
0684: // Validate wheter param is at least min
0685: if (min != null) {
0686: if (getLogger().isDebugEnabled())
0687: getLogger().debug(
0688: "Long parameter " + name
0689: + " should be at least " + min);
0690:
0691: if (min.compareTo(value) > 0) {
0692: if (getLogger().isDebugEnabled())
0693: getLogger().debug("and it is not");
0694: return new ValidatorActionHelper(value,
0695: ValidatorActionResult.TOOSMALL);
0696: }
0697: }
0698:
0699: // Validate wheter param is at most max
0700: if (max != null) {
0701: if (getLogger().isDebugEnabled())
0702: getLogger().debug(
0703: "Long parameter " + name
0704: + " should be at most " + max);
0705: if (max.compareTo(value) < 0) {
0706: if (getLogger().isDebugEnabled())
0707: getLogger().debug("and it is not");
0708: return new ValidatorActionHelper(value,
0709: ValidatorActionResult.TOOLARGE);
0710: }
0711: }
0712: }
0713: return new ValidatorActionHelper(value);
0714: }
0715:
0716: /**
0717: * Validates nullability and default value for given parameter. If given
0718: * constraints are not null they are validated as well.
0719: */
0720: private ValidatorActionHelper validateDouble(String name,
0721: Configuration constraints, Configuration conf, Map params,
0722: boolean is_string, Object param) {
0723:
0724: boolean nullable = getNullable(conf, constraints);
0725: Double value = null;
0726: Double dflt = getDoubleValue(getDefault(conf, constraints),
0727: true);
0728:
0729: if (getLogger().isDebugEnabled())
0730: getLogger().debug(
0731: "Validating double parameter " + name
0732: + " (encoded in a string: " + is_string
0733: + ")");
0734: try {
0735: value = getDoubleValue(param, is_string);
0736: } catch (Exception e) {
0737: // Unable to parse double
0738: return new ValidatorActionHelper(value,
0739: ValidatorActionResult.ERROR);
0740: }
0741: if (value == null) {
0742: if (getLogger().isDebugEnabled())
0743: getLogger().debug(
0744: "double parameter " + name + " is null");
0745: if (!nullable) {
0746: return new ValidatorActionHelper(value,
0747: ValidatorActionResult.ISNULL);
0748: } else {
0749: return new ValidatorActionHelper(dflt);
0750: }
0751: }
0752: if (constraints != null) {
0753: Double eq = getAttributeAsDouble(constraints, "equals-to",
0754: null);
0755: String eqp = constraints
0756: .getAttribute("equals-to-param", "");
0757:
0758: Double min = getAttributeAsDouble(conf, "min", null);
0759: min = getAttributeAsDouble(constraints, "min", min);
0760:
0761: Double max = getAttributeAsDouble(conf, "max", null);
0762: max = getAttributeAsDouble(constraints, "max", max);
0763:
0764: // Validate whether param is equal to constant
0765: if (eq != null) {
0766: if (getLogger().isDebugEnabled())
0767: getLogger().debug(
0768: "Double parameter " + name
0769: + " should be equal to " + eq);
0770:
0771: if (!value.equals(eq)) {
0772: if (getLogger().isDebugEnabled())
0773: getLogger().debug("and it is not");
0774: return new ValidatorActionHelper(value,
0775: ValidatorActionResult.NOMATCH);
0776: }
0777: }
0778:
0779: // Validate whether param is equal to another param
0780: // FIXME: take default value of param being compared with into
0781: // account?
0782: if (!"".equals(eqp)) {
0783: if (getLogger().isDebugEnabled())
0784: getLogger().debug(
0785: "Double parameter " + name
0786: + " should be equal to "
0787: + params.get(eqp));
0788: // Request parameter is stored as string.
0789: // Need to convert it beforehand.
0790: try {
0791: Double _eqp = new Double(Double
0792: .parseDouble((String) params.get(eqp)));
0793: if (!value.equals(_eqp)) {
0794: if (getLogger().isDebugEnabled())
0795: getLogger().debug("and it is not");
0796: return new ValidatorActionHelper(value,
0797: ValidatorActionResult.NOMATCH);
0798: }
0799: } catch (NumberFormatException nfe) {
0800: if (getLogger().isDebugEnabled())
0801: getLogger().debug(
0802: "Double parameter " + name + ": " + eqp
0803: + " is no double", nfe);
0804: return new ValidatorActionHelper(value,
0805: ValidatorActionResult.NOMATCH);
0806: }
0807: }
0808:
0809: // Validate wheter param is at least min
0810: if (min != null) {
0811: if (getLogger().isDebugEnabled())
0812: getLogger().debug(
0813: "Double parameter " + name
0814: + " should be at least " + min);
0815: if (0 > value.compareTo(min)) {
0816: if (getLogger().isDebugEnabled())
0817: getLogger().debug("and it is not");
0818: return new ValidatorActionHelper(value,
0819: ValidatorActionResult.TOOSMALL);
0820: }
0821: }
0822:
0823: // Validate wheter param is at most max
0824: if (max != null) {
0825: if (getLogger().isDebugEnabled())
0826: getLogger().debug(
0827: "Double parameter " + name
0828: + " should be at most " + max);
0829: if (0 < value.compareTo(max)) {
0830: if (getLogger().isDebugEnabled())
0831: getLogger().debug("and it is not");
0832: return new ValidatorActionHelper(value,
0833: ValidatorActionResult.TOOLARGE);
0834: }
0835: }
0836: }
0837: return new ValidatorActionHelper(value);
0838: }
0839:
0840: /**
0841: * Returns the parsed Double value.
0842: */
0843: private Double getDoubleValue(Object param, boolean is_string)
0844: throws ClassCastException, NumberFormatException {
0845:
0846: /* convert param to double */
0847: if (is_string) {
0848: String tmp = getStringValue(param);
0849: if (tmp == null) {
0850: return null;
0851: }
0852: return new Double(tmp);
0853: } else {
0854: return (Double) param;
0855: }
0856: }
0857:
0858: /**
0859: * Returns the parsed Long value.
0860: */
0861: private Long getLongValue(Object param, boolean is_string)
0862: throws ClassCastException, NumberFormatException {
0863:
0864: /* convert param to long */
0865: if (is_string) {
0866: String tmp = getStringValue(param);
0867: if (tmp == null) {
0868: return null;
0869: }
0870: return Long.decode(tmp);
0871: } else {
0872: return (Long) param;
0873: }
0874: }
0875:
0876: /**
0877: * Returns string
0878: * @throws ClassCastException if param is not a String object
0879: */
0880: private String getStringValue(Object param)
0881: throws ClassCastException {
0882:
0883: /* convert param to string */
0884: String value = (String) param;
0885: if (value != null && "".equals(value.trim())) {
0886: value = null;
0887: }
0888: return value;
0889: }
0890:
0891: /**
0892: * Returns the value of 'nullable' attribute from given configuration or
0893: * from given constraints, value present in constraints takes precedence,
0894: * false when attribute is not present in either of them.
0895: */
0896: private boolean getNullable(Configuration conf, Configuration cons) {
0897: /* check nullability */
0898: try {
0899: String tmp = cons.getAttribute("nullable");
0900: return BooleanUtils.toBoolean(tmp);
0901: } catch (Exception e) {
0902: String tmp = "no";
0903: if (conf != null) {
0904: tmp = conf.getAttribute("nullable", "no");
0905: }
0906: return BooleanUtils.toBoolean(tmp);
0907: }
0908: }
0909:
0910: /**
0911: * Returns the default value from given configuration or constraints.
0912: * Value present in constraints takes precedence, null is returned when no
0913: * default attribute is present in eiher of them.
0914: */
0915: private String getDefault(Configuration conf, Configuration cons) {
0916: String dflt = "";
0917: try {
0918: dflt = cons.getAttribute("default");
0919: } catch (Exception e) {
0920: if (conf != null)
0921: dflt = conf.getAttribute("default", "");
0922: }
0923: if ("".equals(dflt.trim())) {
0924: dflt = null;
0925: }
0926: return dflt;
0927: }
0928:
0929: /**
0930: * Replacement for Avalon's Configuration.getAttributeAsLong
0931: * because that one doesn't take <code>Long</code> but long and
0932: * thus won't take <code>null</code> as parameter value for
0933: * default.
0934: *
0935: * @param conf Configuration
0936: * @param name Parameter's name
0937: * @param dflt Default value
0938: * @return Parameter's value in <code>configuration</code> or
0939: * <code>dflt</code> if parameter is not set or couldn't be
0940: * converted to a <code>Long</code>
0941: * @throws NumberFormatException if conversion fails
0942: */
0943: private Long getAttributeAsLong(Configuration conf, String name,
0944: Long dflt) throws NumberFormatException {
0945: try {
0946: return new Long(conf.getAttribute(name));
0947: } catch (ConfigurationException e) {
0948: return dflt;
0949: }
0950: }
0951:
0952: /**
0953: * Addition to Avalon's Configuration.getAttributeAsFloat
0954: * because that one does only deal with <code>float</code>.
0955: *
0956: * @param conf Configuration
0957: * @param name Parameter's name
0958: * @param dflt Default value
0959: * @return Parameter's value in <code>configuration</code> or
0960: * <code>dflt</code> if parameter is not set or couldn't be
0961: * converted to a <code>Double</code>
0962: * @throws NumberFormatException if conversion fails
0963: */
0964: private Double getAttributeAsDouble(Configuration conf,
0965: String name, Double dflt) throws NumberFormatException {
0966: try {
0967: return new Double(conf.getAttribute(name));
0968: } catch (ConfigurationException e) {
0969: return dflt;
0970: }
0971: }
0972:
0973: /**
0974: * Create an index map to an array of configurations by their name
0975: * attribute. An empty array results in an empty map.
0976: *
0977: * @param descriptor
0978: * @return index map or empty map
0979: */
0980: protected Map indexConfiguration(Configuration[] descriptor) {
0981: if (descriptor == null)
0982: return new HashMap();
0983: Map result = new HashMap(
0984: (descriptor.length > 0) ? descriptor.length * 2 : 5);
0985: for (int i = descriptor.length - 1; i >= 0; i--) {
0986: String name = descriptor[i].getAttribute("name", "");
0987: result.put(name, descriptor[i]);
0988: }
0989: return result;
0990: }
0991:
0992: /**
0993: * Recursively resolve constraint sets that may "include" other constraint
0994: * sets and return a collection of all parameters to validate.
0995: *
0996: * @param valsetstr
0997: * @param consets
0998: * @return collection of all parameters to validate
0999: */
1000: protected Collection resolveConstraints(String valsetstr,
1001: Map consets) {
1002: /* get the list of params to be validated */
1003: Vector rules = new Vector();
1004: Configuration[] set = ((Configuration) consets.get(valsetstr))
1005: .getChildren("validate");
1006: for (int j = 0; j < set.length; j++) {
1007: rules.add(set[j]);
1008: }
1009: set = ((Configuration) consets.get(valsetstr))
1010: .getChildren("include");
1011: for (int j = 0; j < set.length; j++) {
1012: Collection tmp = resolveConstraints(set[j].getAttribute(
1013: "name", ""), consets);
1014: rules.addAll(tmp);
1015: }
1016: return rules;
1017: }
1018:
1019: /**
1020: * Checks the default setting for reloading the descriptor file.
1021: * @return boolean
1022: */
1023: protected boolean isDescriptorReloadable() {
1024: // read global parameter settings
1025: boolean reloadable = Constants.DESCRIPTOR_RELOADABLE_DEFAULT;
1026: if (this .settings.containsKey("reloadable")) {
1027: reloadable = Boolean.valueOf(
1028: (String) this .settings.get("reloadable"))
1029: .booleanValue();
1030: }
1031: return reloadable;
1032: }
1033:
1034: /**
1035: * Get list of params to be validated from sitemap parameter and
1036: * isolates the parameter names from the comma separated list.
1037: *
1038: */
1039: protected Collection getSetOfParameterNamesFromSitemap(
1040: String valstr, Map desc) {
1041: String[] rparams = null;
1042: Set set = new HashSet(20);
1043: if (!"*".equals(valstr.trim())) {
1044: rparams = StringUtils.split(valstr, ",");
1045: if (rparams != null) {
1046: for (int i = rparams.length - 1; i >= 0; i--) {
1047: set.add(desc.get(rparams[i]));
1048: }
1049: }
1050: } else {
1051: // validate _all_ parameters
1052: set = desc.entrySet();
1053: }
1054: return set;
1055: }
1056:
1057: /**
1058: * Validate all parameters in the set with the constraints contained in
1059: * desc and the values from params. Validation details are in resultMap and
1060: * successful validated parameters in resultMap.
1061: *
1062: * @param desc
1063: * @param actionMap
1064: * @param resultMap
1065: * @param set
1066: * @param params
1067: * @param isString
1068: * @return boolean all parameters ok or not
1069: */
1070: protected boolean validateSetOfParameters(Map desc, Map actionMap,
1071: Map resultMap, Collection set, Map params, boolean isString) {
1072:
1073: boolean allOK = true;
1074: ValidatorActionHelper result;
1075: String name;
1076: String rule = null;
1077: for (Iterator i = set.iterator(); i.hasNext();) {
1078: Configuration constr = (Configuration) i.next();
1079: name = constr.getAttribute("name", null);
1080: rule = constr.getAttribute("rule", name);
1081: result = validateParameter(name, rule, constr, desc,
1082: params, isString);
1083: if (!result.isOK()) {
1084: if (getLogger().isDebugEnabled())
1085: getLogger().debug(
1086: "Validation failed for parameter " + name);
1087: allOK = false;
1088: }
1089: actionMap.put(name, result.getObject());
1090: resultMap.put(name, result.getResult());
1091: }
1092: return allOK;
1093: }
1094:
1095: /**
1096: * Add success indicator to resulting maps and clear actionMap if unsuccessful.
1097: * Results are stored as request attributes.
1098: *
1099: * @param objectModel the object model
1100: * @param actionMap a Map containing validated parameters
1101: * @param resultMap a Map containing validation results
1102: * @param allOK a boolean indicating if all validations were successful
1103: * @return actionMap if allOK or null otherwise
1104: */
1105: protected Map setResult(Map objectModel, Map actionMap,
1106: Map resultMap, boolean allOK) {
1107: if (!allOK) {
1108: // if any validation failed return an empty map
1109: actionMap = null;
1110: resultMap.put("*", ValidatorActionResult.ERROR);
1111: if (getLogger().isDebugEnabled())
1112: getLogger()
1113: .debug(
1114: "All form params validated. An error occurred.");
1115: } else {
1116: resultMap.put("*", ValidatorActionResult.OK);
1117: if (getLogger().isDebugEnabled())
1118: getLogger().debug(
1119: "All form params successfully validated");
1120: }
1121: // store validation results in request attribute
1122: ObjectModelHelper.getRequest(objectModel).setAttribute(
1123: Constants.XSP_FORMVALIDATOR_PATH, resultMap);
1124: //return Collections.unmodifiableMap (actionMap);
1125: return actionMap;
1126: }
1127:
1128: /**
1129: * Load the descriptor containing the constraints.
1130: * @param resolver
1131: * @param parameters
1132: * @return a Configuration containing the constraints or null if a problem occurred.
1133: */
1134: protected Configuration getDescriptor(SourceResolver resolver,
1135: Map objectModel, Parameters parameters) {
1136: Configuration conf = null;
1137: try {
1138: conf = this .getConfiguration(parameters.getParameter(
1139: "descriptor", (String) this .settings
1140: .get("descriptor")), resolver, parameters
1141: .getParameterAsBoolean("reloadable",
1142: isDescriptorReloadable()));
1143: } catch (ConfigurationException e) {
1144: if (this .getLogger().isWarnEnabled())
1145: this .getLogger().warn("Exception reading descriptor: ",
1146: e);
1147: }
1148: return conf;
1149: }
1150:
1151: }
|