/*
* Copyright 2009-2010 the original author or authors.
*
* 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.internna.iwebmvc.ui;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.directwebremoting.annotations.DataTransferObject;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.validator.Length;
import org.hibernate.validator.NotEmpty;
import org.hibernate.validator.NotNull;
import org.hibernate.validator.Pattern;
import org.hibernate.validator.Size;
import org.internna.iwebmvc.javascript.Scriptable;
import org.internna.iwebmvc.metadata.ui.EntityFilter;
import org.internna.iwebmvc.metadata.ui.FormField;
import org.internna.iwebmvc.metadata.ui.GridColumn;
import org.internna.iwebmvc.model.ui.UIGroup;
import static org.internna.iwebmvc.utils.ClassUtils.call;
import static org.internna.iwebmvc.utils.CollectionUtils.isNotEmpty;
import static org.internna.iwebmvc.utils.ClassUtils.getAnnotatedFields;
import static org.internna.iwebmvc.utils.StringUtils.hasText;
/**
* Utility class to obtain UI components (form fields, entity filters, ...) from a class instance.
*
* @author Jose Noheda
* @since 2.0
*/
public final class UIUtils {
private static final Log logger = LogFactory.getLog(UIUtils.class);
private UIUtils() {
throw new AssertionError("Do not try to instantiate utility classes");
}
/**
* Tries to determine the mapping that the parameter will take in Javascript.
*/
public static String inferDWRConvertedJavascriptClass(Class<?> clazz) {
String inferred = "";
if (clazz != null) {
if (clazz.isAnnotationPresent(DataTransferObject.class)) {
DataTransferObject dto = clazz.getAnnotation(DataTransferObject.class);
inferred = dto.javascript();
}
if (!hasText(inferred)) inferred = clazz.getSimpleName();
}
return inferred;
}
/**
* Checks if the paramter is annotated with @Searchable.
*/
public static boolean isSearchable(final Class<?> clazz) {
return clazz == null ? false : clazz.getAnnotation(Indexed.class) != null;
}
/**
* Checks if the parameter implements Scriptable.
*/
public static boolean isScriptable(final Class<?> clazz) {
return clazz == null ? false : Scriptable.class.isAssignableFrom(clazz);
}
public static UIGroup<EntityFilterImpl> getEntityFilters(final Class<?> clazz) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, IllegalAccessException, InvocationTargetException {
return getUIGroup(clazz, EntityFilter.class, new FieldCallback() {
@Override public Object doWith(Field field) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, IllegalAccessException, InvocationTargetException {
return createEntityFilter(field);
}
});
}
public static UIGroup<GridColumnImpl> getEntityColumns(final Class<?> clazz) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, IllegalAccessException, InvocationTargetException {
return getUIGroup(clazz, GridColumn.class, new FieldCallback() {
@Override public Object doWith(Field field) {
return createGridColumn(field);
}
});
}
public static UIGroup<FormFieldImpl> getFormFields(final Class<?> clazz) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, IllegalAccessException, InvocationTargetException {
return getUIGroup(clazz, FormField.class, new FieldCallback() {
@Override public Object doWith(Field field) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, IllegalAccessException, InvocationTargetException {
return createFormField(field);
}
});
}
private static <T extends Annotation> UIGroup getUIGroup(final Class<?> clazz, final Class<T> annotationClass, final FieldCallback callback) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, IllegalAccessException, InvocationTargetException {
List<Field> matchingFields = getAnnotatedFields(clazz, annotationClass);
Collections.sort(matchingFields, new Comparator<Field>() {
@Override public int compare(Field first, Field second) {
try {
int firstOrder = (Integer) call("order", first.getAnnotation(annotationClass));
int secondOrder = (Integer) call("order", second.getAnnotation(annotationClass));
return firstOrder > secondOrder ? 1 : -1;
} catch (Exception ex) {
return 0;
}
}
});
UIGroup group = createUIGroup(clazz);
if (isNotEmpty(matchingFields))
for(Field field : matchingFields)
group.addComponent(callback.doWith(field));
return group;
}
private static UIGroup createUIGroup(Class<?> clazz) {
UIGroup group = new UIGroup();
group.setEntityClass(clazz);
group.setComponents(new ArrayList());
return group;
}
private static EntityFilterImpl createEntityFilter(Field field) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, IllegalAccessException, InvocationTargetException {
EntityFilterImpl filter = new EntityFilterImpl();
filter.setEmbeddedClass(field.getDeclaringClass());
filter.setPath(field.getName());
EntityFilter entityFilter = field.getAnnotation(EntityFilter.class);
filter.setRange(entityFilter.range());
filter.setType(entityFilter.type());
if (EntityFilter.WIDGET_TYPE.EMBEDDED.equals(filter.getType())) filter.setSubfilters(getSubFilters(field));
filter.setHelp(entityFilter.help());
String[] values = entityFilter.values();
if ((values != null) && (values.length > 0)) filter.setValues(Arrays.asList(values));
if (logger.isDebugEnabled()) logger.debug("Adding filter [" + filter + "] to view");
return filter;
}
private static List<EntityFilterImpl> getSubFilters(Field field) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, IllegalAccessException, InvocationTargetException {
return getEntityFilters(field.getType()).getComponents();
}
private static GridColumnImpl createGridColumn(Field field) {
GridColumnImpl grid = new GridColumnImpl();
grid.setPath(field.getName());
GridColumn column = field.getAnnotation(GridColumn.class);
grid.setEditor(column.editor());
grid.setFormatter(column.formatter());
grid.setRelativeWidth(column.relativeWidth());
return grid;
}
private static FormFieldImpl createFormField(Field field) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, IllegalAccessException, InvocationTargetException {
FormFieldImpl formField = new FormFieldImpl();
formField.setPath(field.getName());
formField.setEmbeddedClass(field.getDeclaringClass());
FormField form = field.getAnnotation(FormField.class);
formField.setType(form.type());
formField.setHelp(form.help());
addValidation(field, formField);
formField.setRenderCrud(form.renderCrud());
if (EntityFilter.WIDGET_TYPE.EMBEDDED.equals(formField.getType())) formField.setSubfields(getSubFields(field));
return formField;
}
private static void addValidation(Field field, FormFieldImpl formField) {
if (field.getAnnotation(NotNull.class) != null) formField.setRequired(true);
if (field.getAnnotation(NotEmpty.class) != null) {
formField.setRequired(true);
formField.setMin(1);
}
if (field.getAnnotation(Size.class) != null) {
Size size = field.getAnnotation(Size.class);
formField.setMin(size.min());
formField.setMax(size.max());
}
if (field.getAnnotation(Length.class) != null) {
Length length = field.getAnnotation(Length.class);
formField.setMin(length.min());
formField.setMax(length.max());
}
if (field.getAnnotation(Pattern.class) != null) {
formField.setRegExp(field.getAnnotation(Pattern.class).regex());
}
}
private static List<FormFieldImpl> getSubFields(Field field) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, IllegalAccessException, InvocationTargetException {
return getFormFields(field.getType()).getComponents();
}
private static interface FieldCallback {
Object doWith(Field fiel) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, IllegalAccessException, InvocationTargetException;
}
}