blob: d38b4e75636024088db00f9333161da97a611efd [file] [log] [blame]
// Copyright 2007, 2008 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.apache.tapestry5.internal.services;
import org.apache.tapestry5.*;
import org.apache.tapestry5.corelib.internal.InternalMessages;
import org.apache.tapestry5.internal.util.Holder;
import org.apache.tapestry5.ioc.Messages;
import org.apache.tapestry5.ioc.internal.util.Defense;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.ioc.services.TypeCoercer;
import org.apache.tapestry5.ioc.util.ExceptionUtils;
import org.apache.tapestry5.services.ValidationMessagesSource;
public class FieldValidationSupportImpl implements FieldValidationSupport
{
static final String PARSE_CLIENT_EVENT = "parseClient";
static final String TO_CLIENT_EVENT = "toClient";
static final String VALIDATE_EVENT = "validate";
private final ValidationMessagesSource messagesSource;
private final TypeCoercer typeCoercer;
public FieldValidationSupportImpl(ValidationMessagesSource messagesSource, TypeCoercer typeCoercer)
{
this.messagesSource = messagesSource;
this.typeCoercer = typeCoercer;
}
@SuppressWarnings({ "unchecked" })
public String toClient(Object value, ComponentResources componentResources, Translator translator,
NullFieldStrategy nullFieldStrategy)
{
Defense.notNull(componentResources, "componentResources");
Defense.notNull(translator, "translator");
Defense.notNull(nullFieldStrategy, "nullFieldStrategy");
final Holder<String> resultHolder = Holder.create();
ComponentEventCallback callback = new ComponentEventCallback()
{
public boolean handleResult(Object result)
{
// What's nice is that the ComponentEventException will automatically identify
// the method description.
if (!(result instanceof String))
throw new RuntimeException(InternalMessages.toClientShouldReturnString());
resultHolder.put((String) result);
return true;
}
};
componentResources.triggerEvent(TO_CLIENT_EVENT, new Object[] { value }, callback);
if (resultHolder.hasValue()) return resultHolder.get();
Object effectiveValue = value;
if (effectiveValue == null)
{
effectiveValue = nullFieldStrategy.replaceToClient();
// Don't try to coerce or translate null.
if (effectiveValue == null) return null;
}
// And now, whether its a value from a bound property, or from the null field strategy,
// get it into the right format for the translator and let it translate.
Object coerced = typeCoercer.coerce(effectiveValue, translator.getType());
return translator.toClient(coerced);
}
public Object parseClient(String clientValue, ComponentResources componentResources, Translator translator,
NullFieldStrategy nullFieldStrategy)
throws ValidationException
{
Defense.notNull(componentResources, "componentResources");
Defense.notNull(translator, "translator");
Defense.notNull(nullFieldStrategy, "nullFieldStrategy");
String effectiveValue = clientValue;
if (InternalUtils.isBlank(effectiveValue))
{
effectiveValue = nullFieldStrategy.replaceFromClient();
if (effectiveValue == null) return null;
}
final Holder<Object> resultHolder = Holder.create();
ComponentEventCallback callback = new ComponentEventCallback()
{
public boolean handleResult(Object result)
{
resultHolder.put(result);
return true;
}
};
try
{
componentResources.triggerEvent(PARSE_CLIENT_EVENT, new Object[] { effectiveValue }, callback);
}
catch (RuntimeException ex)
{
rethrowValidationException(ex);
}
if (resultHolder.hasValue()) return resultHolder.get();
// Otherwise, let the normal translator do the job.
Messages messages = messagesSource.getValidationMessages(componentResources.getLocale());
return translator.parseClient(effectiveValue, messages);
}
/**
* Checks for a {@link org.apache.tapestry5.ValidationException} inside the outer exception and throws that,
* otherwise rethrows the runtime exception.
*
* @param outerException initially caught exception
* @throws ValidationException if found
*/
private void rethrowValidationException(RuntimeException outerException) throws ValidationException
{
ValidationException ve = ExceptionUtils.findCause(outerException, ValidationException.class);
if (ve != null) throw ve;
throw outerException;
}
@SuppressWarnings({ "unchecked" })
public void validate(Object value, ComponentResources componentResources, FieldValidator validator)
throws ValidationException
{
Defense.notNull(componentResources, "componentResources");
Defense.notNull(validator, "validator");
validator.validate(value);
try
{
componentResources.triggerEvent(VALIDATE_EVENT, new Object[] { value }, null);
}
catch (RuntimeException ex)
{
rethrowValidationException(ex);
}
}
}