001// Copyright 2007, 2008, 2010 The Apache Software Foundation 002// 003// Licensed under the Apache License, Version 2.0 (the "License"); 004// you may not use this file except in compliance with the License. 005// You may obtain a copy of the License at 006// 007// http://www.apache.org/licenses/LICENSE-2.0 008// 009// Unless required by applicable law or agreed to in writing, software 010// distributed under the License is distributed on an "AS IS" BASIS, 011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012// See the License for the specific language governing permissions and 013// limitations under the License. 014 015package org.apache.tapestry5.internal.services; 016 017import org.apache.tapestry5.*; 018import org.apache.tapestry5.internal.util.Holder; 019import org.apache.tapestry5.ioc.internal.util.InternalUtils; 020import org.apache.tapestry5.ioc.services.PropertyAccess; 021import org.apache.tapestry5.ioc.services.TypeCoercer; 022import org.apache.tapestry5.ioc.util.ExceptionUtils; 023 024@SuppressWarnings("all") 025public class FieldValidationSupportImpl implements FieldValidationSupport 026{ 027 private final TypeCoercer typeCoercer; 028 029 private final PropertyAccess propertyAccess; 030 031 public FieldValidationSupportImpl(TypeCoercer typeCoercer, PropertyAccess propertyAccess) 032 { 033 this.typeCoercer = typeCoercer; 034 this.propertyAccess = propertyAccess; 035 } 036 037 public String toClient(Object value, ComponentResources componentResources, FieldTranslator<Object> translator, 038 NullFieldStrategy nullFieldStrategy) 039 { 040 assert componentResources != null; 041 assert translator != null; 042 assert nullFieldStrategy != null; 043 final Holder<String> resultHolder = Holder.create(); 044 045 ComponentEventCallback callback = new ComponentEventCallback() 046 { 047 public boolean handleResult(Object result) 048 { 049 // What's nice is that the ComponentEventException will automatically identify 050 // the method description. 051 052 if (!(result instanceof String)) 053 throw new RuntimeException("Return value from 'parseClient' event handler method must be a string."); 054 055 resultHolder.put((String) result); 056 057 return true; 058 } 059 }; 060 061 componentResources.triggerEvent(EventConstants.TO_CLIENT, new Object[] 062 {value}, callback); 063 064 if (resultHolder.hasValue()) 065 return resultHolder.get(); 066 067 Object effectiveValue = value; 068 069 if (effectiveValue == null) 070 { 071 effectiveValue = nullFieldStrategy.replaceToClient(); 072 073 // Don't try to coerce or translate null. 074 075 if (effectiveValue == null) 076 return null; 077 } 078 079 // And now, whether its a value from a bound property, or from the null field strategy, 080 // get it into the right format for the translator and let it translate. 081 082 Object coerced = typeCoercer.coerce(effectiveValue, translator.getType()); 083 084 return translator.toClient(coerced); 085 } 086 087 public Object parseClient(String clientValue, ComponentResources componentResources, 088 FieldTranslator<Object> translator, NullFieldStrategy nullFieldStrategy) throws ValidationException 089 { 090 assert componentResources != null; 091 assert translator != null; 092 assert nullFieldStrategy != null; 093 String effectiveValue = clientValue; 094 095 if (InternalUtils.isBlank(effectiveValue)) 096 { 097 effectiveValue = nullFieldStrategy.replaceFromClient(); 098 099 if (effectiveValue == null) 100 return null; 101 } 102 103 final Holder<Object> resultHolder = Holder.create(); 104 105 ComponentEventCallback callback = new ComponentEventCallback() 106 { 107 public boolean handleResult(Object result) 108 { 109 resultHolder.put(result); 110 return true; 111 } 112 }; 113 114 try 115 { 116 componentResources.triggerEvent(EventConstants.PARSE_CLIENT, new Object[] 117 {effectiveValue}, callback); 118 } catch (RuntimeException ex) 119 { 120 rethrowValidationException(ex); 121 } 122 123 if (resultHolder.hasValue()) 124 return resultHolder.get(); 125 126 return translator.parse(effectiveValue); 127 } 128 129 /** 130 * Checks for a {@link org.apache.tapestry5.ValidationException} inside the outer exception and throws that, 131 * otherwise rethrows the runtime exception. 132 * 133 * @param outerException initially caught exception 134 * @throws ValidationException if found 135 */ 136 private void rethrowValidationException(RuntimeException outerException) throws ValidationException 137 { 138 ValidationException ve = ExceptionUtils.findCause(outerException, ValidationException.class, propertyAccess); 139 140 if (ve != null) 141 throw ve; 142 143 throw outerException; 144 } 145 146 @SuppressWarnings( 147 {"unchecked"}) 148 public void validate(Object value, ComponentResources componentResources, FieldValidator validator) 149 throws ValidationException 150 { 151 assert componentResources != null; 152 assert validator != null; 153 validator.validate(value); 154 155 try 156 { 157 componentResources.triggerEvent(EventConstants.VALIDATE, new Object[] 158 {value}, null); 159 } catch (RuntimeException ex) 160 { 161 rethrowValidationException(ex); 162 } 163 } 164}