/***
* Copyright (c) 2009 Caelum - www.caelum.com.br/opensource
* All rights reserved.
*
* 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 br.com.caelum.vraptor.http.iogi;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import br.com.caelum.iogi.Instantiator;
import br.com.caelum.iogi.MultiInstantiator;
import br.com.caelum.iogi.ObjectInstantiator;
import br.com.caelum.iogi.collections.ArrayInstantiator;
import br.com.caelum.iogi.collections.ListInstantiator;
import br.com.caelum.iogi.conversion.FallbackConverter;
import br.com.caelum.iogi.conversion.StringConverter;
import br.com.caelum.iogi.parameters.Parameter;
import br.com.caelum.iogi.parameters.Parameters;
import br.com.caelum.iogi.reflection.NewObject;
import br.com.caelum.iogi.reflection.Target;
import br.com.caelum.iogi.spi.DependencyProvider;
import br.com.caelum.iogi.spi.ParameterNamesProvider;
import br.com.caelum.vraptor.converter.ConversionException;
import br.com.caelum.vraptor.converter.Converter;
import br.com.caelum.vraptor.core.Converters;
import br.com.caelum.vraptor.http.InvalidParameterException;
import br.com.caelum.vraptor.validator.Message;
import com.google.common.collect.ImmutableList;
@RequestScoped
public class VRaptorInstantiator implements InstantiatorWithErrors, Instantiator<Object> {
private static final Logger logger = LoggerFactory.getLogger(VRaptorInstantiator.class);
private MultiInstantiator multiInstantiator;
private List<Message> errors;
private final Converters converters;
private final DependencyProvider provider;
private final ParameterNamesProvider parameterNameProvider;
private final HttpServletRequest request;
/**
* @deprecated CDI eyes only
*/
protected VRaptorInstantiator() {
this(null, null, null, null);
}
@Inject
public VRaptorInstantiator(Converters converters, DependencyProvider provider,
ParameterNamesProvider parameterNameProvider, HttpServletRequest request) {
this.converters = converters;
this.provider = provider;
this.parameterNameProvider = parameterNameProvider;
this.request = request;
}
@PostConstruct
public void createInstantiator() {
Instantiator<Object> objectInstantiator = new ObjectInstantiator(this, provider, parameterNameProvider);
if (useNullForMissingParameters()) {
objectInstantiator = new NullDecorator(objectInstantiator);
}
List<Instantiator<?>> instantiatorList = ImmutableList.of(
new RequestAttributeInstantiator(request),
new VRaptorTypeConverter(converters),
FallbackConverter.fallbackToNull(new StringConverter()),
new ArrayAdapter(new ArrayInstantiator(this)),
new NullDecorator(new ListInstantiator(this)),
new NullDecorator(new SetInstantiator(this)),
new DependencyInstantiator(),
objectInstantiator);
multiInstantiator = new MultiInstantiator(instantiatorList);
}
protected boolean useNullForMissingParameters() {
return false;
}
@Override
public boolean isAbleToInstantiate(Target<?> target) {
return true;
}
@Override
public Object instantiate(Target<?> target, Parameters parameters, List<Message> errors) {
this.errors = errors;
return instantiate(target, parameters);
}
@Override
public Object instantiate(Target<?> target, Parameters parameters) {
try {
return multiInstantiator.instantiate(target, parameters);
} catch(Exception e) {
handleException(target, e);
return null;
}
}
private void handleException(Target<?> target, Throwable e) {
if (e.getCause() == null) {
throw new InvalidParameterException("Exception when trying to instantiate " + target, e);
} else {
handleException(target, e.getCause());
}
}
private final class DependencyInstantiator implements Instantiator<Object> {
@Override
public Object instantiate(Target<?> target, Parameters params) {
return provider.provide(target);
}
@Override
public boolean isAbleToInstantiate(Target<?> target) {
return target.getClassType().isInterface() && provider.canProvide(target);
}
}
private final class VRaptorTypeConverter implements Instantiator<Object> {
private final Converters converters;
public VRaptorTypeConverter(Converters converters) {
this.converters = converters;
}
@Override
public boolean isAbleToInstantiate(Target<?> target) {
return converters.existsFor(target.getClassType());
}
@Override
public Object instantiate(Target<?> target, Parameters parameters) {
try {
if(isSupportedCollection(target)) {
return multiInstantiator.instantiate(target, parameters);
}
Parameter parameter = parameters.namedAfter(target);
return converterForTarget(target).convert(parameter.getValue(), target.getClassType());
} catch (ConversionException ex) {
logger.debug("Could not convert target", ex);
errors.add(ex.getValidationMessage().withCategory(target.getName()));
} catch (IllegalStateException e) {
logger.debug("An error occured while getting validation message", e);
return setPropertiesAfterConversions(target, parameters);
}
return null;
}
private boolean isSupportedCollection(Target<?> target) {
return List.class.isAssignableFrom(target.getClassType())
|| Set.class.isAssignableFrom(target.getClassType())
|| Map.class.isAssignableFrom(target.getClassType());
}
private Object setPropertiesAfterConversions(Target<?> target, Parameters parameters) {
List<Parameter> params = parameters.forTarget(target);
Parameter parameter = findParamFor(params, target);
Object converted = converterForTarget(target).convert(parameter == null? null : parameter.getValue(), target.getClassType());
return new NewObject(this, parameters.focusedOn(target), converted).valueWithPropertiesSet();
}
private Parameter findParamFor(List<Parameter> params, Target<?> target) {
for (Parameter parameter : params) {
if (parameter.getName().equals(target.getName())) {
return parameter;
}
}
return null;
}
@SuppressWarnings("unchecked")
private Converter<Object> converterForTarget(Target<?> target) {
return (Converter<Object>) converters.to(target.getClassType());
}
}
}