/* * Copyright 2000-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jetspeed.util.parser; import java.beans.PropertyDescriptor; import java.beans.IndexedPropertyDescriptor; import java.beans.Introspector; import java.lang.reflect.Method; import java.math.BigDecimal; import org.apache.torque.om.NumberKey; import org.apache.torque.om.StringKey; import java.util.Date; import org.apache.turbine.util.ParameterParser; import org.apache.turbine.util.pool.Recyclable; import org.apache.jetspeed.util.ValidationException; import org.apache.jetspeed.util.ValidationHelper; /** * A Turbine parameter parser with Validation built in. * Validates any bean with methods that begin with validate[AttributeName]. * Works with Torque-generated beans. * To use this class, override the TurbineResources.properties: * * services.RunDataService.default.parameter.parser=org.apache.turbine.util.parser.DefaultParameterParser * * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a> * @version $Id: ValidationParameterParser.java,v 1.6 2004/02/23 03:18:08 jford Exp $ */ public class ValidationParameterParser extends DefaultJetspeedParameterParser implements ParameterParser, Recyclable { public ValidationParameterParser() { super(); } public ValidationParameterParser(String characterEncoding) { super (characterEncoding); } /** * Uses bean introspection to set writable properties of bean from * the parameters, where a (case-insensitive) name match between * the bean property and the parameter is looked for. * * @param bean An Object. * @exception Exception, a generic exception. */ public void setProperties(Object bean) throws Exception { Class beanClass = bean.getClass(); PropertyDescriptor[] props = Introspector.getBeanInfo(beanClass).getPropertyDescriptors(); StringBuffer invalidFieldMessages = new StringBuffer(""); boolean valid = true; for (int i = 0; i < props.length; i++) { String propname = props[i].getName(); Method setter = props[i].getWriteMethod(); if (setter != null && (containsKey(propname) || containsDateSelectorKeys(propname) || containsTimeSelectorKeys(propname))) { /* VALIDATION */ if (!validateProperty(bean, props[i])) { invalidFieldMessages.append("'"); invalidFieldMessages.append(propname); invalidFieldMessages.append("' is not a valid field, "); valid = false; } setMyProperty(bean, props[i]); } } // call the general validation for the entire business object String msg = generalValidation(bean); if (msg != null) { invalidFieldMessages.append(msg); invalidFieldMessages.append(", "); valid = false; } //System.out.println("invalidFieldMessages ["+invalidFieldMessages+"]"); if (!valid) { //int lastComma = invalidFieldMessages.lastIndexOf(", "); int lastComma = new String(invalidFieldMessages).lastIndexOf(", "); String result = invalidFieldMessages.substring(0,lastComma); // System.err.println("Had a validation problem and am throwing: "+invalidFieldMessages); throw new ValidationException(result); } } /** * Set the property 'prop' in the bean to the value of the * corresponding parameters. Supports all types supported by * getXXX methods plus a few more that come for free because * primitives have to be wrapped before being passed to invoke * anyway. * * @param bean An Object. * @param prop A PropertyDescriptor. * @exception Exception, a generic exception. */ protected void setMyProperty(Object bean, PropertyDescriptor prop) throws Exception { if (prop instanceof IndexedPropertyDescriptor) { throw new Exception(prop.getName() + " is an indexed property (not supported)"); } Method setter = prop.getWriteMethod(); if (setter == null) { throw new Exception(prop.getName() + " is a read only property"); } Object[] args = getArguments(prop); try { setter.invoke(bean, args); } catch (Throwable t) { System.out.println("Validation: EXCEPTION (prop): " + prop.getName()); } } protected Object[] getArguments(PropertyDescriptor prop) throws Exception { Class propclass = prop.getPropertyType(); Object[] args = { null }; if (propclass == String.class) { args[0] = getString(prop.getName()); } else if (propclass == Integer.class || propclass == Integer.TYPE) { args[0] = getInteger(prop.getName()); } else if (propclass == Short.class || propclass == Short.TYPE) { args[0] = new Short((short)(getInteger(prop.getName()).intValue())); } else if (propclass == Long.class || propclass == Long.TYPE) { args[0] = new Long(getLong(prop.getName())); } else if (propclass == Boolean.class || propclass == Boolean.TYPE) { args[0] = getBool(prop.getName()); } else if (propclass == Double.class || propclass == Double.TYPE) { args[0] = new Double(getDouble(prop.getName())); } else if (propclass == BigDecimal.class) { args[0] = getBigDecimal(prop.getName()); } else if (propclass == String[].class) { args[0] = getStrings(prop.getName()); } else if (propclass == Object.class) { args[0] = getObject(prop.getName()); } else if (propclass == int[].class) { args[0] = getInts(prop.getName()); } else if (propclass == Integer[].class) { args[0] = getIntegers(prop.getName()); } else if (propclass == Date.class) { args[0] = getDate(prop.getName()); } else if (propclass == NumberKey.class) { args[0] = getNumberKey(prop.getName()); } else if (propclass == StringKey.class) { args[0] = getStringKey(prop.getName()); } else { //throw new Exception("property " + prop.getName() + " is of unsupported type " + propclass.toString()); } return args; } /** * Validate a bean's property based on definition in the business object * * @param bean The bean to be validated. * @param prop The bean's property descriptor * @return true if validation was successful, false if validation failed **/ protected boolean validateProperty(Object bean, PropertyDescriptor prop) throws Exception { String propertyName = prop.getName(); String methodName = "validate" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1); Class[] signatureParams = { prop.getPropertyType() }; //{ java.lang.String.class }; Object[] methodParams = getArguments(prop); try { Method method = bean.getClass().getMethod(methodName, signatureParams); boolean isValidBool = ((Boolean)method.invoke(bean, methodParams)).booleanValue(); return isValidBool; } catch (NoSuchMethodException nsm_e) { try{ return validateObject(prop); } catch (Exception e) { // System.err.println("EXCEPTION With the auto validation "+ e); return false; } } catch (Exception e) { // System.err.println("EXCEPTION INVOKING METHOD " + methodName + " : " + e); } return true; } protected boolean validateObject(PropertyDescriptor prop) throws Exception { Class propclass = prop.getPropertyType(); Object[] args = { null }; if (propclass == String.class) { return ValidationHelper.isAlphaNumeric(getString(prop.getName()), false); } else if (propclass == Integer.class || propclass == Integer.TYPE) { return ValidationHelper.isInteger(getString(prop.getName()), false); } else if (propclass == Short.class || propclass == Short.TYPE) { return ValidationHelper.isInteger(getString(prop.getName()), false); } else if (propclass == Long.class || propclass == Long.TYPE) { return ValidationHelper.isDecimal(getString(prop.getName()), false); } else if (propclass == Boolean.class || propclass == Boolean.TYPE) { return true;// WORK NEEDED } else if (propclass == Double.class || propclass == Double.TYPE) { return ValidationHelper.isDecimal(getString(prop.getName()), false); } else if (propclass == BigDecimal.class) { return ValidationHelper.isDecimal(getString(prop.getName()), false); } else if (propclass == String[].class) { return ValidationHelper.isAlphaNumeric(getString(prop.getName()), false); } else if (propclass == Object.class) { System.err.println("Auto validate: Object-- NOT IMPLEMENTED"); return true;//work needed } else if (propclass == int[].class) { return ValidationHelper.isInteger(getString(prop.getName()), false); } else if (propclass == Integer[].class) { return ValidationHelper.isInteger(getString(prop.getName()), false); } else if (propclass == Date.class) { // System.err.println("Auto validate: Date -- NOT IMPLEMENTED"); return true;//work needed } else if (propclass == NumberKey.class) { return ValidationHelper.isInteger(getString(prop.getName()), false); } else if (propclass == StringKey.class) { return ValidationHelper.isInteger(getString(prop.getName()), false); } else { /* throw new Exception("property " + prop.getName() + " is of unsupported type " + propclass.toString()); */ } return false; } /** * Validate a bean's property based on definition in the business object * * @param bean The bean to be validated. * @param prop The bean's property descriptor * @return null if validation was successful, an error message if validation failed **/ protected String generalValidation(Object bean) throws Exception { String methodName = "validate"; try { Method method = bean.getClass().getMethod(methodName, null); String msg = (String)method.invoke(bean, null); return msg; } catch (NoSuchMethodException nsm_e) { } catch (Exception e) { System.err.println("EXCEPTION INVOKING METHOD " + methodName + " : " + e); } return null; } }