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    }