    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.commons.services.PropertyAccess;
    019import org.apache.tapestry5.commons.services.TypeCoercer;
    020import org.apache.tapestry5.commons.util.ExceptionUtils;
    021import org.apache.tapestry5.internal.util.Holder;
    022import org.apache.tapestry5.ioc.internal.util.InternalUtils;
    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}