    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    }