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.corelib.internal.InternalMessages;
019import org.apache.tapestry5.internal.util.Holder;
020import org.apache.tapestry5.ioc.internal.util.InternalUtils;
021import org.apache.tapestry5.ioc.services.PropertyAccess;
022import org.apache.tapestry5.ioc.services.TypeCoercer;
023import org.apache.tapestry5.ioc.util.ExceptionUtils;
024
025@SuppressWarnings("all")
026public 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}