package com.psddev.cms.view;
import com.psddev.dari.util.TypeDefinition;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @deprecated Use {@link ViewModel} instead.
*/
@Deprecated
public interface ViewCreator<M, V, VR> {
/**
* @deprecated Use {@link ViewModel#onCreate(ViewResponse)} instead.
*/
@Deprecated
V createView(M model, VR request);
/**
* @deprecated Use {@link ViewModel#onCreate(ViewResponse)} instead.
*/
@Deprecated
default boolean processRequest(VR request, ViewResponse response) {
return true;
}
/**
* @deprecated Use {@link ViewModel#findViewModelClass(Class, String, Object)} instead.
*/
@Deprecated
static <M, V, VR> Class<? extends ViewCreator<? super M, V, ? super VR>> findCreatorClass(M model, Class<V> viewClass, String viewType, VR viewRequest) {
if (model == null) {
return null;
}
Class<?> modelClass = model.getClass();
Map<Class<?>, List<Class<? extends ViewCreator>>> modelToCreatorClassMap = new HashMap<>();
Set<Class<? extends ViewCreator>> allCreatorClasses = new LinkedHashSet<>();
for (Class<?> annotatableClass : ViewUtils.getAnnotatableClasses(modelClass)) {
allCreatorClasses.addAll(
Arrays.stream(annotatableClass.getAnnotationsByType(ViewMapping.class))
// check that it matches the view type if it exists
.filter(viewMapping -> viewType == null || Arrays.asList(viewMapping.types()).contains(viewType))
// get the annotation's view creator class
.map(ViewMapping::value)
// make sure it's a concrete class
.filter(klass -> !Modifier.isAbstract(klass.getModifiers()) && !Modifier.isInterface(klass.getModifiers()))
.collect(Collectors.toList()));
if (viewType == null && annotatableClass.isAnnotationPresent(StaticNestedViewMappings.class)) {
allCreatorClasses.addAll(
Arrays.stream(annotatableClass.getClasses())
// make sure it's a view creator class
.filter(ViewCreator.class::isAssignableFrom)
// make sure it's a concrete class
.filter(klass -> !Modifier.isAbstract(klass.getModifiers()) && !Modifier.isInterface(klass.getModifiers()))
// cast it
.map(klass -> (Class<? extends ViewCreator>) klass)
.collect(Collectors.toList()));
}
}
allCreatorClasses.forEach(creatorClass -> {
TypeDefinition<? extends ViewCreator> typeDef = TypeDefinition.getInstance(creatorClass);
Class<?> declaredModelClass = typeDef.getInferredGenericTypeArgumentClass(ViewCreator.class, 0);
Class<?> declaredViewClass = typeDef.getInferredGenericTypeArgumentClass(ViewCreator.class, 1);
Class<?> declaredViewRequestClass = typeDef.getInferredGenericTypeArgumentClass(ViewCreator.class, 2);
if ((viewClass == null || (declaredViewClass != null && viewClass.isAssignableFrom(declaredViewClass)) || viewClass.isAssignableFrom(creatorClass))
&& declaredModelClass != null && declaredModelClass.isAssignableFrom(modelClass)
&& (viewRequest == null || declaredViewRequestClass != null && declaredViewRequestClass.isAssignableFrom(viewRequest.getClass()))) {
List<Class<? extends ViewCreator>> creatorClasses = modelToCreatorClassMap.get(declaredModelClass);
if (creatorClasses == null) {
creatorClasses = new ArrayList<>();
modelToCreatorClassMap.put(declaredModelClass, creatorClasses);
}
creatorClasses.add(creatorClass);
}
});
if (!modelToCreatorClassMap.isEmpty()) {
Set<Class<?>> nearestModelClasses = ViewUtils.getNearestSuperClassesInSet(model.getClass(), modelToCreatorClassMap.keySet());
if (nearestModelClasses.size() == 1) {
List<Class<? extends ViewCreator>> creatorClasses = modelToCreatorClassMap.get(nearestModelClasses.iterator().next());
if (creatorClasses.size() == 1) {
@SuppressWarnings("unchecked")
Class<? extends ViewCreator<? super M, V, ? super VR>> creatorClass = (Class<? extends ViewCreator<? super M, V, ? super VR>>) (Object) creatorClasses.get(0);
return creatorClass;
} else {
LoggerFactory.getLogger(ViewCreator.class)
.warn("Found " + creatorClasses.size()
+ " conflicting view creator mappings for model type ["
+ model.getClass() + "] and view type [" + viewClass + "]: "
+ creatorClasses);
}
} else {
Set<Class<? extends ViewCreator>> conflictingCreatorClasses = new LinkedHashSet<>();
for (Class<?> nearestModelClass : nearestModelClasses) {
conflictingCreatorClasses.addAll(modelToCreatorClassMap.get(nearestModelClass));
}
LoggerFactory.getLogger(ViewCreator.class)
.warn("Found " + conflictingCreatorClasses.size()
+ " conflicting view creator mappings for model type ["
+ model.getClass() + "] and view type [" + viewClass + "]: "
+ conflictingCreatorClasses);
}
}
return null;
}
/**
* @deprecated Use {@link #findCreatorClass(Object, Class, String, Object)} instead.
*/
@Deprecated
static <M, V> ViewCreator<M, V, Object> createCreator(M model, Class<V> viewClass) {
if (model == null) {
return null;
}
Class<?> modelClass = model.getClass();
Map<Class<?>, List<Class<? extends ViewCreator>>> modelToCreatorClassMap = new HashMap<>();
Set<Class<? extends ViewCreator>> allCreatorClasses = new LinkedHashSet<>();
for (Class<?> annotatableClass : ViewUtils.getAnnotatableClasses(modelClass)) {
allCreatorClasses.addAll(
Arrays.stream(annotatableClass.getAnnotationsByType(ViewMapping.class))
.map(ViewMapping::value)
.collect(Collectors.toList()));
if (annotatableClass.isAnnotationPresent(StaticNestedViewMappings.class)) {
allCreatorClasses.addAll(
Arrays.stream(annotatableClass.getClasses())
.filter(ViewCreator.class::isAssignableFrom)
.map((klass) -> (Class<? extends ViewCreator>) klass)
.collect(Collectors.toList()));
}
}
allCreatorClasses.forEach(creatorClass -> {
TypeDefinition<? extends ViewCreator> typeDef = TypeDefinition.getInstance(creatorClass);
Class<?> declaredModelClass = typeDef.getInferredGenericTypeArgumentClass(ViewCreator.class, 0);
Class<?> declaredViewClass = typeDef.getInferredGenericTypeArgumentClass(ViewCreator.class, 1);
if (((declaredViewClass != null && viewClass.isAssignableFrom(declaredViewClass)) || viewClass.isAssignableFrom(creatorClass))
&& declaredModelClass != null && declaredModelClass.isAssignableFrom(modelClass)) {
List<Class<? extends ViewCreator>> creatorClasses = modelToCreatorClassMap.get(declaredModelClass);
if (creatorClasses == null) {
creatorClasses = new ArrayList<>();
modelToCreatorClassMap.put(declaredModelClass, creatorClasses);
}
creatorClasses.add(creatorClass);
}
});
if (!modelToCreatorClassMap.isEmpty()) {
Set<Class<?>> nearestModelClasses = ViewUtils.getNearestSuperClassesInSet(model.getClass(), modelToCreatorClassMap.keySet());
if (nearestModelClasses.size() == 1) {
List<Class<? extends ViewCreator>> creatorClasses = modelToCreatorClassMap.get(nearestModelClasses.iterator().next());
if (creatorClasses.size() == 1) {
Class<? extends ViewCreator> creatorClass = creatorClasses.get(0);
try {
@SuppressWarnings("unchecked")
ViewCreator<M, V, Object> creator = (ViewCreator<M, V, Object>) TypeDefinition.getInstance(creatorClass).newInstance();
return creator;
} catch (Exception e) {
LoggerFactory.getLogger(ViewCreator.class)
.warn("Unable to create view creator of type [" + creatorClass.getName() + "]");
}
} else {
LoggerFactory.getLogger(ViewCreator.class)
.warn("Found " + creatorClasses.size()
+ " conflicting view creator mappings for model type ["
+ model.getClass() + "] and view type [" + viewClass + "]: "
+ creatorClasses);
}
} else {
Set<Class<? extends ViewCreator>> conflictingCreatorClasses = new LinkedHashSet<>();
for (Class<?> nearestModelClass : nearestModelClasses) {
conflictingCreatorClasses.addAll(modelToCreatorClassMap.get(nearestModelClass));
}
LoggerFactory.getLogger(ViewCreator.class)
.warn("Found " + conflictingCreatorClasses.size()
+ " conflicting view creator mappings for model type ["
+ model.getClass() + "] and view type [" + viewClass + "]: "
+ conflictingCreatorClasses);
}
} else {
LoggerFactory.getLogger(ViewCreator.class)
.warn("No view creator mappings found for model type ["
+ model.getClass() + "] and view type [" + viewClass + "]");
}
return null;
}
/**
* @deprecated Use {@link #findCreatorClass(Object, Class, String, Object)} instead.
*/
@Deprecated
static <M> ViewCreator<M, ?, Object> createCreator(M model, String viewType) {
for (Class<?> c : ViewUtils.getAnnotatableClasses(model.getClass())) {
for (ViewMapping mapping : c.getAnnotationsByType(ViewMapping.class)) {
Optional<String> name = Arrays.stream(mapping.types())
.filter(n -> n.equals(viewType))
.findFirst();
if (name.isPresent()) {
return (ViewCreator<M, ?, Object>) TypeDefinition.getInstance(mapping.value()).newInstance();
}
}
}
return null;
}
}