ViewUtil.java
package org.xandercat.pmdb.util;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.servlet.http.HttpSession;
import org.springframework.data.util.ReflectionUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.MapBindingResult;
import org.springframework.web.multipart.MultipartFile;
import org.xandercat.pmdb.dto.Movie;
import org.xandercat.pmdb.form.Option;
import org.xandercat.pmdb.service.CollectionService;
import org.xandercat.pmdb.service.ImdbAttribute;
import org.xandercat.pmdb.util.format.AbstractDataFormatter;
import org.xandercat.pmdb.util.format.DataFormatter;
import org.xandercat.pmdb.util.format.DataFormatterSelector;
import org.xandercat.pmdb.util.format.DataFormatters;
import org.xandercat.pmdb.util.format.DateFormatter;
import org.xandercat.pmdb.util.format.DoubleFormatter;
import org.xandercat.pmdb.util.format.LongFormatter;
import org.xandercat.pmdb.util.format.StringFormatter;
/**
* Utility methods for interacting with the view.
*
* @author Scott Arnold
*/
public class ViewUtil {
public static String TAB_HOME = "isHomeTab";
public static String TAB_COLLECTIONS = "isCollectionsTab";
public static String TAB_IMDB_SEARCH = "isImdbSearchTab";
public static String TAB_USER_ADMIN = "isUserAdminTab";
public static String TAB_MY_ACCOUNT = "isMyAccountTab";
public static String SESSION_NUM_SHARE_OFFERS_KEY = "numShareOffers";
public static String SESSION_COLLECTION_UPLOAD_FILE = "importedCollectionFile";
public static String SESSION_COLLECTION_UPLOAD_SHEETS = "importedCollectionSheetNames";
public static String SESSION_COLLECTION_UPLOAD_COLUMNS = "importedCollectionColumnNames";
public static void updateNumShareOffers(CollectionService collectionService, HttpSession session, String username) {
int numShareOffers = collectionService.getShareOfferMovieCollections(username).size();
if (numShareOffers > 0) {
session.setAttribute(SESSION_NUM_SHARE_OFFERS_KEY, Integer.valueOf(numShareOffers));
} else {
session.removeAttribute(SESSION_NUM_SHARE_OFFERS_KEY);
}
}
public static MultipartFile getImportedCollectionFile(HttpSession session) {
return (MultipartFile) session.getAttribute(SESSION_COLLECTION_UPLOAD_FILE);
}
@SuppressWarnings("unchecked")
public static List<String> getImportedCollectionSheets(HttpSession session) {
return (List<String>) session.getAttribute(SESSION_COLLECTION_UPLOAD_SHEETS);
}
@SuppressWarnings("unchecked")
public static List<String> getImportedCollectionColumns(HttpSession session) {
return (List<String>) session.getAttribute(SESSION_COLLECTION_UPLOAD_COLUMNS);
}
public static void setImportedCollectionFile(HttpSession session, MultipartFile multipartFile, List<String> sheets, List<String> columns) {
session.setAttribute(SESSION_COLLECTION_UPLOAD_FILE, multipartFile);
session.setAttribute(SESSION_COLLECTION_UPLOAD_SHEETS, sheets);
session.setAttribute(SESSION_COLLECTION_UPLOAD_COLUMNS, columns);
}
public static void clearImportedCollectionFile(HttpSession session) {
session.removeAttribute(SESSION_COLLECTION_UPLOAD_FILE);
session.removeAttribute(SESSION_COLLECTION_UPLOAD_SHEETS);
session.removeAttribute(SESSION_COLLECTION_UPLOAD_COLUMNS);
}
private static Option reflectOptionWithIntValue(Object item, String valueGetterName, String textGetterName) {
try {
Method valueMethod = ReflectionUtils.findRequiredMethod(item.getClass(), valueGetterName, (Class<?>[]) null);
Method textMethod = ReflectionUtils.findRequiredMethod(item.getClass(), textGetterName, (Class<?>[]) null);
Object value = valueMethod.invoke(item, (Object[]) null);
Object text = textMethod.invoke(item, (Object[]) null);
return new Option(value, text);
} catch (Exception e) {
throw new IllegalArgumentException("Unable to obtain option text and value with reflection.");
}
}
private static <E extends Enum<E>> Option getOption(E e) {
return new Option(e.name(), e.toString());
}
private static Option getOption(String s) {
return new Option(s, s);
}
/**
* Shortcut method to return list of options for view given collection of objects. Value field is currently
* limited to primitive int type. Text field is currently limited to String type.
*
* @param items items to create list of options for
* @param valueGetterName getter method name for the value (int type)
* @param textGetterName getter method name for the text (String type)
*
* @return list of options for the items
*/
public static List<Option> getOptions(Collection<?> items, String valueGetterName, String textGetterName) {
return items.stream().map(item -> {
return reflectOptionWithIntValue(item, valueGetterName, textGetterName);
}).collect(Collectors.toList());
}
/**
* Shortcut method to return list of options for the given enum class type. Enum name will be used for
* value, and toString() will be used for text.
*
* @param enumType enum class
* @return list of options for enum class
*/
public static <E extends Enum<E>> List<Option> getOptions(Class<E> enumType) {
return Arrays.stream(enumType.getEnumConstants()).map(ViewUtil::getOption).collect(Collectors.toList());
}
/**
* Shortcut method to return list of options for the given collection of strings. Each String
* will be used for both the value and text of it's returned option.
*
* @param strings strings to create options for
* @return options for the strings
*/
public static List<Option> getOptions(Collection<String> strings) {
return strings.stream().map(ViewUtil::getOption).collect(Collectors.toList());
}
/**
* Returns an empty binding result.
*
* @param objectName object name
*
* @return empty binding result
*/
public static BindingResult emptyBindingResult(String objectName) {
return new MapBindingResult(new HashMap<String, String>(), objectName);
}
/**
* Returns DataFormatters for the given collection of movies that is suitable for use with
* Thymeleaf and Datatables.net sortable table.
*
* @param movies collection of movies
*
* @return DataFormatters for collection of movies
*/
public static DataFormatters getDataFormatters(Collection<Movie> movies, Collection<String> attributeNames) {
DataFormatters dataFormatters = new DataFormatters();
dataFormatters.setGenericFormatter(stringFormatter());
dataFormatters.addAttributeFormatter(ImdbAttribute.IMDB_VOTES.getKey(), longFormatter());
dataFormatters.addAttributeFormatter(ImdbAttribute.IMDB_RATING.getKey(), doubleFormatter(1));
dataFormatters.addAttributeFormatter(ImdbAttribute.RELEASED.getKey(), dateFormatter("dd MMM yyyy"));
dataFormatters.addAttributeFormatter(ImdbAttribute.YEAR.getKey(), stringFormatter()); // will prevent long formatter from trying to long format the year
// create general data formatters for remaining attributes
List<DataFormatter> generalDataFormatters = new ArrayList<DataFormatter>();
generalDataFormatters.add(longFormatter());
generalDataFormatters.add(doubleFormatter(3));
generalDataFormatters.add(dateFormatter(
"MM/dd/yyyy", "yyyy/MM/dd", "MM-dd-yyyy", "yyyy-MM-dd", "M/d/yyyyy", "dd MMM yyyy"));
// use selectors to select data formatters for remaining attributes
Set<String> remainingAttributeNames = new HashSet<String>();
remainingAttributeNames.addAll(attributeNames);
remainingAttributeNames.removeAll(dataFormatters.getAttributeFormatters().keySet());
List<DataFormatterSelector> selectors = remainingAttributeNames.stream()
.map(attrName -> new DataFormatterSelector(attrName, generalDataFormatters))
.collect(Collectors.toList());
selectors.stream().forEach(selector -> {
movies.stream().forEach(movie -> selector.test(movie.getAttribute(selector.getAttributeName())));
Optional<DataFormatter> dataFormatter = selector.getDataFormatter();
if (dataFormatter.isPresent()) {
dataFormatters.addAttributeFormatter(selector.getAttributeName(), dataFormatter.get());
}
});
return dataFormatters;
}
private static AbstractDataFormatter<String> stringFormatter() {
// to ensure datatables works with Thymeleaf, need to have blank space for default sort value
return new StringFormatter()
.defaultDisplayValue("").defaultSortValue(" ");
}
private static AbstractDataFormatter<Long> longFormatter() {
return new LongFormatter()
.defaultDisplayValue("").defaultSortValue("0");
}
private static AbstractDataFormatter<Double> doubleFormatter(int maximumFractionDigits) {
return new DoubleFormatter(maximumFractionDigits)
.defaultDisplayValue("").defaultSortValue("0.0").displayFormattedValue(false);
}
private static AbstractDataFormatter<Date> dateFormatter(String... parseFormats) {
return new DateFormatter().parseFormats(parseFormats).defaultDisplayValue("").defaultSortValue(" ");
}
}