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 }