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 015 package org.apache.tapestry5.internal.services; 016 017 import org.apache.tapestry5.*; 018 import org.apache.tapestry5.corelib.internal.InternalMessages; 019 import org.apache.tapestry5.internal.util.Holder; 020 import org.apache.tapestry5.ioc.internal.util.InternalUtils; 021 import org.apache.tapestry5.ioc.services.PropertyAccess; 022 import org.apache.tapestry5.ioc.services.TypeCoercer; 023 import org.apache.tapestry5.ioc.util.ExceptionUtils; 024 025 @SuppressWarnings("all") 026 public class FieldValidationSupportImpl implements FieldValidationSupport 027 { 028 private final TypeCoercer typeCoercer; 029 030 private final PropertyAccess propertyAccess; 031 032 public FieldValidationSupportImpl(TypeCoercer typeCoercer, PropertyAccess propertyAccess) 033 { 034 this.typeCoercer = typeCoercer; 035 this.propertyAccess = propertyAccess; 036 } 037 038 public String toClient(Object value, ComponentResources componentResources, FieldTranslator<Object> translator, 039 NullFieldStrategy nullFieldStrategy) 040 { 041 assert componentResources != null; 042 assert translator != null; 043 assert nullFieldStrategy != null; 044 final Holder<String> resultHolder = Holder.create(); 045 046 ComponentEventCallback callback = new ComponentEventCallback() 047 { 048 public boolean handleResult(Object result) 049 { 050 // What's nice is that the ComponentEventException will automatically identify 051 // the method description. 052 053 if (!(result instanceof String)) 054 throw new RuntimeException(InternalMessages.toClientShouldReturnString()); 055 056 resultHolder.put((String) result); 057 058 return true; 059 } 060 }; 061 062 componentResources.triggerEvent(EventConstants.TO_CLIENT, new Object[] 063 {value}, callback); 064 065 if (resultHolder.hasValue()) 066 return resultHolder.get(); 067 068 Object effectiveValue = value; 069 070 if (effectiveValue == null) 071 { 072 effectiveValue = nullFieldStrategy.replaceToClient(); 073 074 // Don't try to coerce or translate null. 075 076 if (effectiveValue == null) 077 return null; 078 } 079 080 // And now, whether its a value from a bound property, or from the null field strategy, 081 // get it into the right format for the translator and let it translate. 082 083 Object coerced = typeCoercer.coerce(effectiveValue, translator.getType()); 084 085 return translator.toClient(coerced); 086 } 087 088 public Object parseClient(String clientValue, ComponentResources componentResources, 089 FieldTranslator<Object> translator, NullFieldStrategy nullFieldStrategy) throws ValidationException 090 { 091 assert componentResources != null; 092 assert translator != null; 093 assert nullFieldStrategy != null; 094 String effectiveValue = clientValue; 095 096 if (InternalUtils.isBlank(effectiveValue)) 097 { 098 effectiveValue = nullFieldStrategy.replaceFromClient(); 099 100 if (effectiveValue == null) 101 return null; 102 } 103 104 final Holder<Object> resultHolder = Holder.create(); 105 106 ComponentEventCallback callback = new ComponentEventCallback() 107 { 108 public boolean handleResult(Object result) 109 { 110 resultHolder.put(result); 111 return true; 112 } 113 }; 114 115 try 116 { 117 componentResources.triggerEvent(EventConstants.PARSE_CLIENT, new Object[] 118 {effectiveValue}, callback); 119 } catch (RuntimeException ex) 120 { 121 rethrowValidationException(ex); 122 } 123 124 if (resultHolder.hasValue()) 125 return resultHolder.get(); 126 127 return translator.parse(effectiveValue); 128 } 129 130 /** 131 * Checks for a {@link org.apache.tapestry5.ValidationException} inside the outer exception and throws that, 132 * otherwise rethrows the runtime exception. 133 * 134 * @param outerException initially caught exception 135 * @throws ValidationException if found 136 */ 137 private void rethrowValidationException(RuntimeException outerException) throws ValidationException 138 { 139 ValidationException ve = ExceptionUtils.findCause(outerException, ValidationException.class, propertyAccess); 140 141 if (ve != null) 142 throw ve; 143 144 throw outerException; 145 } 146 147 @SuppressWarnings( 148 {"unchecked"}) 149 public void validate(Object value, ComponentResources componentResources, FieldValidator validator) 150 throws ValidationException 151 { 152 assert componentResources != null; 153 assert validator != null; 154 validator.validate(value); 155 156 try 157 { 158 componentResources.triggerEvent(EventConstants.VALIDATE, new Object[] 159 {value}, null); 160 } catch (RuntimeException ex) 161 { 162 rethrowValidationException(ex); 163 } 164 } 165 }