package com.revolsys.ui.html.builder; import java.io.IOException; import java.lang.reflect.Method; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.Callable; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.PreDestroy; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.jexl.Expression; import org.apache.commons.jexl.JexlContext; import org.apache.commons.jexl.context.HashMapContext; import org.apache.log4j.Logger; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.context.ServletContextAware; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.servlet.HandlerMapping; import com.revolsys.collection.ResultPager; import com.revolsys.collection.list.Lists; import com.revolsys.collection.map.Maps; import com.revolsys.io.Reader; import com.revolsys.logging.Logs; import com.revolsys.record.Record; import com.revolsys.record.io.format.json.Json; import com.revolsys.record.io.format.xml.XmlWriter; import com.revolsys.record.query.Query; import com.revolsys.record.schema.RecordStore; import com.revolsys.transaction.Transaction; import com.revolsys.ui.html.decorator.Decorator; import com.revolsys.ui.html.decorator.FieldLabelDecorator; import com.revolsys.ui.html.decorator.FormGroupDecorator; import com.revolsys.ui.html.fields.Field; import com.revolsys.ui.html.fields.LongField; import com.revolsys.ui.html.fields.TextField; import com.revolsys.ui.html.form.Form; import com.revolsys.ui.html.form.HtmlUiBuilderObjectForm; import com.revolsys.ui.html.form.UiBuilderObjectForm; import com.revolsys.ui.html.serializer.BuilderMethodSerializer; import com.revolsys.ui.html.serializer.BuilderSerializer; import com.revolsys.ui.html.serializer.KeySerializerDetailSerializer; import com.revolsys.ui.html.serializer.KeySerializerTableSerializer; import com.revolsys.ui.html.serializer.RowsTableSerializer; import com.revolsys.ui.html.serializer.key.KeySerializer; import com.revolsys.ui.html.serializer.type.BooleanSerializer; import com.revolsys.ui.html.serializer.type.DateSerializer; import com.revolsys.ui.html.serializer.type.DateTimeSerializer; import com.revolsys.ui.html.serializer.type.TimestampSerializer; import com.revolsys.ui.html.serializer.type.TypeSerializer; import com.revolsys.ui.html.view.ButtonsToolbarElement; import com.revolsys.ui.html.view.DetailView; import com.revolsys.ui.html.view.Element; import com.revolsys.ui.html.view.ElementContainer; import com.revolsys.ui.html.view.ElementLabel; import com.revolsys.ui.html.view.RawContent; import com.revolsys.ui.html.view.Script; import com.revolsys.ui.html.view.TabElementContainer; import com.revolsys.ui.html.view.TableView; import com.revolsys.ui.model.Menu; import com.revolsys.ui.web.annotation.ColumnSortOrder; import com.revolsys.ui.web.annotation.RequestMapping; import com.revolsys.ui.web.config.Page; import com.revolsys.ui.web.config.WebUiContext; import com.revolsys.ui.web.exception.PageNotFoundException; import com.revolsys.ui.web.rest.interceptor.MediaTypeUtil; import com.revolsys.ui.web.utils.HttpServletUtils; import com.revolsys.util.CaseConverter; import com.revolsys.util.Exceptions; import com.revolsys.util.HtmlAttr; import com.revolsys.util.HtmlElem; import com.revolsys.util.JavaBeanUtil; import com.revolsys.util.JexlUtil; import com.revolsys.util.Property; import com.revolsys.util.Strings; import com.revolsys.util.UrlUtil; @ResponseStatus(reason = "Access Denied", value = HttpStatus.FORBIDDEN) public class HtmlUiBuilder<T> implements BeanFactoryAware, ServletContextAware { private static final Pattern LINK_KEY_PATTERN = Pattern.compile("link\\(([\\w/]+),([\\w.]+)\\)"); private static final Pattern SUB_KEY_PATTERN = Pattern.compile("^([\\w]+)(?:\\.(.+))?"); public static HttpServletRequest getRequest() { final ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder .getRequestAttributes(); final HttpServletRequest request = requestAttributes.getRequest(); return request; } protected static String getUriTemplateVariable(final String name) { final Map<String, String> parameters = getUriTemplateVariables(); return parameters.get(name); } public static Map<String, String> getUriTemplateVariables() { final HttpServletRequest request = getRequest(); @SuppressWarnings("unchecked") final Map<String, String> uriTemplateVariables = (Map<String, String>)request .getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); if (uriTemplateVariables == null) { return Collections.emptyMap(); } else { return uriTemplateVariables; } } public static boolean isDataTableCallback() { return HttpServletUtils.getParameter("_") != null && HttpServletUtils.getParameter("callback") == null; } public static boolean isDataTableCallback(final HttpServletRequest request) { return request.getParameter("_") != null; } public static boolean isHtmlPage(final HttpServletRequest request) { return MediaTypeUtil.isPreferedMediaType(request, MediaType.TEXT_HTML); } private BeanFactory beanFactory; private HtmlUiBuilderFactory builderFactory; private Map<Class<?>, TypeSerializer> classSerializers = new HashMap<>(); private int defaultPageSize = 25; private Map<String, String> fieldInstructions = new HashMap<>(); private Map<String, Decorator> fieldLabels = new HashMap<>(); private Map<String, Element> fields = new HashMap<>(); private String idParameterName; private String idPropertyName = "id"; /** The map of key lists for list viewSerializers. */ private Map<String, List<String>> keyLists = new HashMap<>(); private Map<String, KeySerializer> keySerializers = new HashMap<>(); private Map<String, String> labels = new HashMap<>(); private final Map<String, List<List<Object>>> listSortOrder = new HashMap<>(); private Logger log = Logger.getLogger(getClass()); private int maxPageSize = 100; private Map<String, String> messages; private Map<String, String> nullLabels = new HashMap<>(); private Map<String, Page> pagesByName = new HashMap<>(); private Map<String, String> pageUrls = new HashMap<>(); private String pluralTitle; private String title; private String typeName; private boolean usePathVariables = true; private Map<String, List<KeySerializer>> viewSerializers = new HashMap<>(); public HtmlUiBuilder() { initLabels(); initSerializers(); initFields(); initPages(); } public HtmlUiBuilder(final String typeName, final String title) { this(typeName, title, title); } public HtmlUiBuilder(final String typeName, final String title, final String pluralTitle) { this(); setTypeName(typeName); this.title = title; this.pluralTitle = pluralTitle; } public void addField(final Field field) { final String name = field.getName(); this.fields.put(name, field); } public void addKeySerializer(final KeySerializer keySerializer) { this.keySerializers.put(keySerializer.getName(), keySerializer); } public void addLabel(final String key, final String label) { this.labels.put(key, label); } protected void addListMenuItems(final Menu menu, final String prefix) { } public void addMenuElement(final ElementContainer container, final Menu menu) { if (menu.getMenus().size() > 0) { final ButtonsToolbarElement buttonsToolbar = new ButtonsToolbarElement(menu); container.add(buttonsToolbar); } } public Menu addMenuItem(final Menu menu, final String prefix, final String pageName, final String linkTitle) { final Map<String, Object> parameters = Collections.<String, Object> emptyMap(); return addMenuItem(menu, prefix, pageName, linkTitle, parameters); } public Menu addMenuItem(final Menu menu, final String prefix, final String pageName, final String linkTitle, final Map<String, Object> parameters) { return addMenuItem(menu, prefix, pageName, linkTitle, null, parameters); } public Menu addMenuItem(final Menu menu, final String prefix, final String pageName, final String linkTitle, final String target) { final Map<String, Object> parameters = Collections.<String, Object> emptyMap(); return addMenuItem(menu, prefix, pageName, linkTitle, target, parameters); } public Menu addMenuItem(final Menu menu, final String prefix, final String pageName, final String linkTitle, final String target, final Map<String, Object> parameters) { final Page page = getPage(prefix, pageName); if (page != null) { final String url = page.getFullUrl(parameters); if (url != null) { final Menu menuItem = new Menu(linkTitle, url); menuItem.setTarget(target); menu.addMenuItem(menuItem); return menuItem; } } return null; } public void addMessageView(final ElementContainer view, final String messageName, final Map<String, Object> variables) { final String message = getMessage(messageName, variables); if (message != null) { view.add(new RawContent(message)); } } /** * @param key * @param label */ public void addNullLabel(final String key, final String label) { this.nullLabels.put(key, label); } public void addObjectViewPage(final TabElementContainer tabs, final Object object, final String prefix) { final HttpServletRequest request = getRequest(); if (object == null) { throw new PageNotFoundException(); } else { final String pageName = getName(prefix, "view"); final Page page = getPage(pageName); if (page == null) { throw new PageNotFoundException("Page not found " + pageName); } else { final List<KeySerializer> serializers = getSerializers(pageName, "view"); final Element detailView = newDetailView(object, serializers); setPageTitle(request, pageName); final Menu actionMenu = new Menu(); final Menu editMenu = addMenuItem(actionMenu, prefix, "edit", "Edit", "_top"); if (editMenu != null) { editMenu.addProperty("buttonClass", "btn-primary"); } final ElementContainer view = new ElementContainer(detailView); addMenuElement(view, actionMenu); final String tabId = getTypeName() + "_" + pageName; final String title = getPageTitle(pageName); tabs.add(tabId, title, view); } } } public void addPage(final Page page) { final String pageName = page.getName(); this.pagesByName.put(pageName, page); } public void addTabDataTable(final TabElementContainer container, final Object builderName, final String pageName, Map<String, Object> parameters) { final HtmlUiBuilder<?> builder = getBuilder(builderName); if (builder != null) { parameters = new HashMap<>(parameters); parameters.put("deferLoading", 0); parameters.put("tabbed", true); parameters.put("scrollYPercent", 1); final HttpServletRequest request = getRequest(); final ElementContainer element = builder.newDataTable(request, pageName, parameters); if (element != null) { final String tabId = builder.getTypeName() + "_" + pageName; final String title = builder.getPageTitle(pageName); container.add(tabId, title, element); } } } protected Record convertRecord(final Record record) { return record; } @PreDestroy public void destroy() { this.beanFactory = null; this.builderFactory = null; this.classSerializers = null; this.fieldInstructions = null; this.fieldLabels = null; this.fields = null; this.idParameterName = null; this.idPropertyName = null; this.keyLists = null; this.keySerializers = null; this.labels = null; this.log = null; this.messages = null; this.nullLabels = null; this.pagesByName = null; this.pageUrls = null; this.pluralTitle = null; this.title = null; this.typeName = null; this.viewSerializers = null; } /** * Construct a new new field (or element) for the named key. The parameters from the * HttpRequest can be used to customise the look of the field. * * @param request The servlet request. * @param key The field key. * @return The generated field element. */ public Element getAttribute(final HttpServletRequest request, final String key) { if (key.equals("id")) { return new LongField("id", false); } else if (this.fields.containsKey(key)) { final Element field = this.fields.get(key); return field.clone(); } else { final TextField field = new TextField(key, false); return field; } } public Decorator getAttributeFormGroupLabel(final String key, final Element element) { final String label = getLabel(key, element); final String instructions = getAttributeInstruction(key); return new FormGroupDecorator(label, instructions); } public String getAttributeInstruction(final String key) { return this.fieldInstructions.get(key); } public Map<String, String> getAttributeInstructions() { return this.fieldInstructions; } public Decorator getAttributeLabel(final String key, final Element element) { final Map<String, Decorator> fieldLabels = getAttributeLabels(); Decorator fieldLabel = fieldLabels.get(key); if (fieldLabel == null) { final String label = getLabel(key, element); final String instructions = getAttributeInstruction(key); if (element instanceof Field) { fieldLabel = new FieldLabelDecorator(label, instructions); } else { fieldLabel = new ElementLabel(label, instructions); } fieldLabels.put(key, fieldLabel); } return fieldLabel; } public Map<String, Decorator> getAttributeLabels() { return this.fieldLabels; } public Map<String, Element> getAttributes() { return this.fields; } /** * @return Returns the beanFactory. */ protected final BeanFactory getBeanFactory() { return this.beanFactory; } /** * Get the HTML UI Builder for the object's class. * * @param objectClass<?> The object class. * @return The builder. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public <H extends HtmlUiBuilder<?>> H getBuilder(final Class<?> objectClass) { if (this.builderFactory != null) { return this.builderFactory.get(objectClass); } else { final HtmlUiBuilder htmlUiBuilder = HtmlUiBuilderFactory.get(this.beanFactory, objectClass); return (H)htmlUiBuilder; } } /** * Get the HTML UI Builder for the object's class. * * @param object The object. * @return The builder. */ @SuppressWarnings("unchecked") public <H extends HtmlUiBuilder<?>> H getBuilder(final Object object) { if (object != null) { if (object instanceof HtmlUiBuilder) { return (H)object; } else if (object instanceof CharSequence) { final String typeName = object.toString(); return (H)getBuilder(typeName); } else if (object instanceof Class) { final Class<H> class1 = (Class<H>)object; @SuppressWarnings("rawtypes") final HtmlUiBuilder builder = getBuilder(class1); return (H)builder; } else { final Class<H> class1 = (Class<H>)object.getClass(); @SuppressWarnings("rawtypes") final HtmlUiBuilder builder = getBuilder(class1); return (H)builder; } } else { return null; } } /** * Get the HTML UI Builder for the class. * * @param className The name of the class. * @return The builder. */ @SuppressWarnings("unchecked") public <H extends HtmlUiBuilder<?>> H getBuilder(final String typeName) { @SuppressWarnings("rawtypes") final HtmlUiBuilder htmlUiBuilder = HtmlUiBuilderFactory.get(this.beanFactory, typeName); return (H)htmlUiBuilder; } /** * Get the factory used to get related HTML UI builders, * * @return The factory. */ public HtmlUiBuilderFactory getBuilderFactory() { return this.builderFactory; } public Map<String, Boolean> getDataTableSortOrder(final List<String> columnNames, final HttpServletRequest request) { final Map<String, Boolean> sortOrder = new LinkedHashMap<>(); for (int i = 0;; i++) { final String columnIndex = request.getParameter("order[" + i + "][column]"); final String direction = request.getParameter("order[" + i + "][dir]"); if (Property.hasValue(columnIndex) && Property.hasValue(direction)) { final String columnName = columnNames.get(Integer.valueOf(columnIndex)); final Boolean sortDir = "asc".equalsIgnoreCase(direction); sortOrder.put(columnName, sortDir); } else { return sortOrder; } } } public int getDefaultPageSize() { return this.defaultPageSize; } /** * @return Returns the idParameterName. */ public String getIdParameterName() { return this.idParameterName; } public String getIdPropertyName() { return this.idPropertyName; } public Object getIdValue(final Object object) { return Property.get(object, this.idPropertyName); } /** * Get the key list with the specified name, or the default if not defined. * * @param name The name of the key list. * @return The key list. */ public List<String> getKeyList(final String name) { return getKeyListOrDefault(this.keyLists, name, "default"); } /** * Get the key list with the specified name, or the list for defaultName if * not defined. * * @param name The name of the key list. * @param defaultName The name of the default key list to use. * @return The key list. */ public List<String> getKeyList(final String name, final String defaultName) { return getKeyListOrDefault(this.keyLists, name, defaultName); } /** * Get the key list with the specified name, or the default if not defined. * * @param keyLists The map of key lists. * @param name The name of the key list. * @param defaultName The name of the default key List * @return The key list. */ private List<String> getKeyListOrDefault(final Map<String, List<String>> keyLists, final String name, final String defaultName) { List<String> keyList = keyLists.get(name); if (keyList == null) { keyList = keyLists.get(defaultName); if (keyList == null) { return Collections.emptyList(); } } return keyList; } /** * Get the map of key lists. * * @return The map of key lists. */ public Map<String, List<String>> getKeyLists() { return this.keyLists; } public KeySerializer getKeySerializer(final String name) { return this.keySerializers.get(name); } /** * @return Returns the keySerializers. */ public Map<String, KeySerializer> getKeySerializers() { return this.keySerializers; } /** * <p> * Get the label for the key. The following process is used (in sequence) to * get the label for the key. * </p> * <ol> * <li>An explict label defined in {@link #setLabels(Map)}</li> * <li>The label for the propetry name portion of a sub key (e.g. For the key * "organization.name" the property name portion is "organization", so the * label would be the label for the key "organization").</li> * <li>The label for the link text key of a link key (e.g. For the key * "Link(view, id)" the link text key is "id", so the label would be the label * for the key "id"</li> * <li>The key converted to Upper Case Words.</li> * </ol> * <p> * After the first call for a particular key the calculated labels are cached. * </p> * * @param key The key. * @return The label. */ public String getLabel(final String key) { final Map<String, String> labels = getLabels(); String label = labels.get(key); if (label == null) { final Matcher linkKeyMatcher = LINK_KEY_PATTERN.matcher(key); if (linkKeyMatcher.find()) { label = getLabel(linkKeyMatcher.group(2)); } else { final Matcher subKeyMatcher = SUB_KEY_PATTERN.matcher(key); if (subKeyMatcher.find() && subKeyMatcher.group(2) != null) { label = getLabel(subKeyMatcher.group(1)); } else { label = CaseConverter.toCapitalizedWords(key); } } addLabel(key, label); } return label; } public String getLabel(final String key, final Element element) { final Map<String, String> labels = getLabels(); String label = labels.get(key); if (label == null) { if (element instanceof Field) { final Field field = (Field)element; label = field.getLabel(); } if (label == null) { return getLabel(key); } else { labels.put(key, label); } } return label; } public Map<String, String> getLabels() { return this.labels; } public Map<String, List<List<Object>>> getListSortOrder() { return this.listSortOrder; } public List<List<Object>> getListSortOrder(final String pageName) { final List<List<Object>> sortOrder = this.listSortOrder.get(pageName); if (sortOrder == null) { if (pageName.equals("list")) { return Collections.emptyList(); } else { return getListSortOrder("list"); } } else { return sortOrder; } } public int getMaxPageSize() { return this.maxPageSize; } public String getMessage(final String messageName) { return this.messages.get(messageName); } public String getMessage(final String messageName, final Map<String, Object> variables) { final String message = getMessage(messageName); if (message != null) { try { final Expression expression = JexlUtil.newExpression(message); if (expression != null) { final JexlContext context = new HashMapContext(); context.setVars(variables); return (String)expression.evaluate(context); } } catch (final Throwable e) { this.log.error(e.getMessage(), e); } } return message; } private String getName(final String prefix, final String keyListName) { if (Property.hasValue(prefix)) { return prefix + CaseConverter.toUpperFirstChar(keyListName); } else { return keyListName; } } private String getNullLabel(final String key) { return this.nullLabels.get(key); } /** * @return Returns the nullLabels. */ public Map<String, String> getNullLabels() { return this.nullLabels; } public Page getPage(final String path) { Page linkPage = this.pagesByName.get(path); if (linkPage == null) { final WebUiContext webUiContext = WebUiContext.get(); if (webUiContext != null) { final Page page = webUiContext.getPage(); if (page != null) { linkPage = page.getPage(path); } } if (linkPage == null) { final String pageByName = this.pageUrls.get(path); if (pageByName != null) { linkPage = new Page(null, getPluralTitle(), pageByName, false); } } } return linkPage; } protected Page getPage(final String prefix, final String name) { final String pageName = getName(prefix, name); Page viewPage = getPage(pageName); if (viewPage == null) { viewPage = getPage(name); } return viewPage; } public Map<String, Page> getPagesByName() { return this.pagesByName; } public String getPageTitle(final String pageName) { final Page page = getPage(pageName); if (page == null) { return null; } else { final String title = page.getExpandedTitle(); return title; } } public String getPageUrl(final String name) { final Map<String, Object> parameters = Collections.emptyMap(); return getPageUrl(name, parameters); } public String getPageUrl(final String name, final Map<String, ? extends Object> parameters) { final Page page = getPage(name); if (page == null) { return null; } else { final String url = page.getFullUrl(parameters); return url; } } public String getPageUrlOld(final String name) { final String url = this.pageUrls.get(name); return url; } public Map<String, String> getPageUrls() { return this.pageUrls; } public String getPluralTitle() { return this.pluralTitle; } public Object getProperty(final Object object, final String keyName) { try { return Property.get(object, keyName); } catch (final Throwable e) { this.log.error("Unable to get property " + keyName + " for:\n" + object, e); return "ERROR"; } } public ResultPager<T> getResultPager(final Map<String, Object> filter) { throw new UnsupportedOperationException(); } protected List<KeySerializer> getSerializers(final String viewName) { final List<KeySerializer> serializers = this.viewSerializers.get(viewName); if (Property.isEmpty(serializers)) { final List<String> elements = getKeyList(viewName); if (!Property.isEmpty(elements)) { newView(viewName, elements); } } return serializers; } protected List<KeySerializer> getSerializers(final String viewName, final String defaultViewName) { List<KeySerializer> serializers = getSerializers(viewName); if (serializers == null) { serializers = getSerializers(defaultViewName); if (serializers == null) { serializers = Collections.emptyList(); } else { this.viewSerializers.put(viewName, serializers); } } return serializers; } public String getTitle() { return this.title; } public String getTypeName() { return this.typeName; } public boolean hasPageUrl(final String pageName) { return this.pageUrls.containsKey(pageName); } protected void initFields() { } public void initializeForm(final HtmlUiBuilderObjectForm form, final HttpServletRequest request) { } public void initializeForm(final UiBuilderObjectForm form, final HttpServletRequest request) { } protected void initLabels() { } protected void initPages() { final Class<?> clazz = getClass(); final Method[] methods = clazz.getMethods(); for (final Method method : methods) { final String name = method.getName(); final RequestMapping requestMapping = method.getAnnotation(RequestMapping.class); if (requestMapping != null) { String pageName = requestMapping.name(); if (Property.isEmpty(pageName)) { pageName = name; } String title = requestMapping.title(); if (Property.isEmpty(title)) { title = CaseConverter.toCapitalizedWords(pageName); } final boolean secure = requestMapping.secure(); String path = null; final String[] paths = requestMapping.value(); if (paths.length > 0) { path = paths[0]; } final String[] fieldNames = requestMapping.fieldNames(); if (fieldNames.length > 0) { newView(pageName, Arrays.asList(fieldNames)); newKeyList(pageName, Arrays.asList(fieldNames)); final ColumnSortOrder[] columnSortOrder = requestMapping.columnSortOrder(); final List<List<Object>> tableSortOrder = new ArrayList<>(); for (final ColumnSortOrder sortOrder : columnSortOrder) { int fieldIndex = -1; final String sortFieldName = sortOrder.value(); for (int i = 0; i < fieldNames.length; i++) { final String fieldName = fieldNames[i]; if (sortFieldName.equals(fieldName)) { fieldIndex = i; } } final boolean ascending = sortOrder.ascending(); String direction; if (ascending) { direction = "asc"; } else { direction = "desc"; } tableSortOrder.add(Arrays.asList(fieldIndex, direction)); } if (!tableSortOrder.isEmpty()) { setListSortOrder(pageName, tableSortOrder); } } final String permission = requestMapping.permission(); final Page page = new Page(pageName, title, path, secure); page.setPermission(permission); addPage(page); } } } protected void initSerializers() { final Class<?> clazz = getClass(); final Method[] methods = clazz.getMethods(); for (final Method method : methods) { final String name = method.getName(); final Class<?>[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length == 2) { if (parameterTypes[0] == XmlWriter.class) { if (parameterTypes[1] == Object.class) { final BuilderMethodSerializer keySerializer = new BuilderMethodSerializer(name, this, method); final String title = this.labels.get(name); if (title != null) { keySerializer.setLabel(title); } addKeySerializer(keySerializer); } } } } this.classSerializers.put(Date.class, new DateTimeSerializer()); this.classSerializers.put(java.sql.Date.class, new DateSerializer()); this.classSerializers.put(Timestamp.class, new TimestampSerializer()); this.classSerializers.put(Boolean.class, new BooleanSerializer()); setListSortOrder(0, true); } protected void insertObject(final T object) { } public boolean isUsePathVariables() { return this.usePathVariables; } public T loadObject(final Object id) { throw new UnsupportedOperationException(); } public ElementContainer newDataTable(final HttpServletRequest request, final String pageName, final Map<String, ? extends Object> parameters) { final String pageUrl = getPageUrl(pageName); if (Property.hasValue(pageUrl)) { final Map<String, Object> params = new HashMap<>(); params.putAll(parameters); params.put("ajaxSource", pageUrl.replaceAll("/+$", ".json")); final List<T> rows = Collections.emptyList(); final ElementContainer table = newDataTable(request, pageName, params, rows); return table; } else { return null; } } public ElementContainer newDataTable(final HttpServletRequest request, final String pageName, Map<String, ? extends Object> parameters, final Collection<? extends Object> rows) { parameters = new HashMap<>(parameters); final List<KeySerializer> serializers = getSerializers(pageName, "list"); final RowsTableSerializer model = new KeySerializerTableSerializer(serializers, rows); final String typeName = getTypeName(); final TableView tableView = new TableView(model, typeName + " dataTable no-footer"); tableView.setWidth("100%"); final String tableId = typeName + "_" + pageName + "_table"; tableView.setId(tableId); tableView.setNoRecordsMessgae(null); final Map<String, Object> tableParams = new LinkedHashMap<>(); tableParams.put("stateSave", true); tableParams.put("autoWidth", true); tableParams.put("dom", Maps.getString(parameters, "dom", "frtiS")); final Boolean ordering = Maps.getBoolean(parameters, "ordering"); if (ordering != null) { tableParams.put("ordering", ordering); } final String scrollY = (String)parameters.get("scrollY"); if (scrollY == null) { tableParams.put("scrollY", "300px"); } else { tableParams.put("scrollY", scrollY); } final String scrollX = (String)parameters.get("scrollX"); if (scrollX == null) { tableParams.put("scrollX", "true"); } else { tableParams.put("scrollX", scrollX); } tableParams.put("order", getListSortOrder(pageName)); Boolean serverSide = (Boolean)parameters.get("serverSide"); final String ajaxSource = (String)parameters.get("ajaxSource"); if (Property.hasValue(ajaxSource)) { if (serverSide == null) { serverSide = true; } tableParams.put("serverSide", serverSide); tableParams.put("ajax", ajaxSource); } else if (serverSide == null) { serverSide = false; } tableParams.put("scroller", Collections.singletonMap("loadingIndicator", true)); final List<Map<String, Object>> columnDefs = new ArrayList<>(); int i = 0; for (final KeySerializer serializer : serializers) { final Map<String, Object> columnDef = new LinkedHashMap<>(); columnDef.put("targets", Arrays.asList(i)); columnDef.put("name", serializer.getKey()); columnDef.put("className", serializer.getKey().replaceAll("[^A-Za-z0-9]", "_")); columnDef.put("title", serializer.getLabel()); final Boolean sortable = serializer.getProperty("sortable"); if (sortable != null) { columnDef.put("orderable", sortable); } final Boolean searchable = serializer.getProperty("searchable"); if (searchable != null) { columnDef.put("searchable", searchable); } final Boolean visible = serializer.getProperty("visible"); if (visible != null) { columnDef.put("visible", visible); } final String width = serializer.getWidth(); if (width != null) { columnDef.put("width", width); } columnDefs.add(columnDef); i++; } if (!columnDefs.isEmpty()) { tableParams.put("columnDefs", columnDefs); } Number scrollYPercent = (Number)parameters.get("scrollYPercent"); if (scrollYPercent == null) { if (scrollY == null) { scrollYPercent = 0; } else { scrollYPercent = 0.98; } } final Script script = new Script(); String jsonMap = Json.toString(tableParams); jsonMap = jsonMap.substring(0, jsonMap.length() - 1) + ",\"createdRow\": function( row, data, dataIndex ) {refreshButtons(row);}" + ",\"initComplete\": function() {$(this).DataTable().columns.adjust();}"; jsonMap += "}"; final StringBuffer scriptBody = new StringBuffer(); scriptBody.append("$(document).ready(function() {\n"); scriptBody.append(" var tableDiv = $('#"); scriptBody.append(tableId); scriptBody.append(" table');\n"); scriptBody.append(" var table = tableDiv.dataTable("); scriptBody.append(jsonMap); scriptBody.append("\n );\n"); scriptBody.append(" tableShowEvents(table,"); scriptBody.append(scrollYPercent); scriptBody.append(");\n"); scriptBody .append("$(window).bind('resize', function () {$(table).DataTable().columns.adjust();} );"); scriptBody.append("});"); script.setContent(scriptBody.toString()); final ElementContainer container = new ElementContainer(tableView, script); final String prefix = pageName.replaceAll("[lL]ist$", ""); final Menu actionMenu = new Menu(); final Menu addMenuItem = addMenuItem(actionMenu, prefix, "add", "Add", "_top"); if (addMenuItem != null) { addMenuItem.addProperty("buttonClass", "btn-primary"); } addListMenuItems(actionMenu, prefix); addMenuElement(container, actionMenu); return container; } public Object newDataTableHandler(final HttpServletRequest request, final String pageName, final Callable<Collection<? extends Object>> rowsCallable) { final Map<String, Object> parameters = new HashMap<>(); return newDataTableHandler(request, pageName, parameters, rowsCallable); } public Object newDataTableHandler(final HttpServletRequest request, final String pageName, final Collection<? extends Object> rows) { final Map<String, Object> parameters = new HashMap<>(); return newDataTableHandler(request, pageName, parameters, rows); } public Object newDataTableHandler(final HttpServletRequest request, final String pageName, final Map<String, Object> parameters, final Callable<Collection<? extends Object>> rowsCallable) { parameters.put("serverSide", false); if (isDataTableCallback(request)) { try { final Collection<? extends Object> rows = rowsCallable.call(); return newDataTableMap(request, rows, pageName); } catch (final Exception e) { throw new RuntimeException("Unable to get rows", e); } } else { final TabElementContainer tabs = new TabElementContainer(); addTabDataTable(tabs, this, pageName, parameters); return tabs; } } public Object newDataTableHandler(final HttpServletRequest request, final String pageName, final Map<String, Object> parameters, final Collection<? extends Object> rows) { if (isDataTableCallback(request)) { return newDataTableMap(request, rows, pageName); } else { parameters.put("serverSide", false); final TabElementContainer tabs = new TabElementContainer(); addTabDataTable(tabs, this, pageName, parameters); return tabs; } } public Object newDataTableHandler(final String pageName, final Collection<? extends Object> rows) { final HttpServletRequest request = HttpServletUtils.getRequest(); return newDataTableHandler(request, pageName, rows); } public Object newDataTableHandlerOrRedirect(final HttpServletRequest request, final HttpServletResponse response, final String pageName, final Collection<? extends Object> rows, final Object parentBuilder, final String parentPageName) { if (isDataTableCallback(request)) { try { return newDataTableMap(request, rows, pageName); } catch (final Exception e) { throw new RuntimeException("Unable to get rows", e); } } else { return redirectToTab(parentBuilder, parentPageName, pageName); } } public Object newDataTableHandlerOrRedirect(final HttpServletRequest request, final String pageName, final Callable<Collection<? extends Object>> rowsCallable, final Object parentBuilder, final String parentPageName) { if (isDataTableCallback(request)) { try { final Collection<? extends Object> rows = rowsCallable.call(); return newDataTableMap(request, rows, pageName); } catch (final Exception e) { return Exceptions.throwUncheckedException(e); } } else { return redirectToTab(parentBuilder, parentPageName, pageName); } } public Map<String, Object> newDataTableMap(final Collection<? extends Object> records, final String pageName) { final HttpServletRequest request = HttpServletUtils.getRequest(); return newDataTableMap(request, records, pageName); } public Map<String, Object> newDataTableMap(final HttpServletRequest request, final Collection<? extends Object> records, final String pageName) { final List<KeySerializer> serializers = getSerializers(pageName, "list"); final List<List<String>> rows = new ArrayList<>(); for (final Object object : records) { final List<String> row = new ArrayList<>(); for (final KeySerializer serializer : serializers) { final String html = serializer.toString(object); row.add(html); } rows.add(row); } final Map<String, Object> response = new LinkedHashMap<>(); final int recordCount = rows.size(); response.put("draw", HttpServletUtils.getIntegerParameter(request, "draw")); response.put("recordsTotal", recordCount); response.put("recordsFiltered", recordCount); response.put("data", rows); return response; } public Map<String, Object> newDataTableMap(final HttpServletRequest request, final RecordStore recordStore, Query query, final String pageName) { final Map<String, Object> response = new LinkedHashMap<>(); try { final int numRecords = recordStore.getRecordCount(query); int recordCount = 50; final String lengthString = request.getParameter("length"); if (Property.hasValue(lengthString)) { if (!"NAN".equalsIgnoreCase(lengthString)) { try { recordCount = Integer.valueOf(lengthString); } catch (final Throwable e) { } } } final int offset = HttpServletUtils.getIntegerParameter(request, "start"); query = query.clone(); query.setOffset(offset); query.setLimit(recordCount); final List<KeySerializer> serializers = getSerializers(pageName, "list"); final List<List<String>> rows = new ArrayList<>(); try ( Reader<Record> reader = recordStore.getRecords(query)) { for (Record record : reader) { record = convertRecord(record); final List<String> row = new ArrayList<>(); for (final KeySerializer serializer : serializers) { final String html = serializer.toString(record); row.add(html); } rows.add(row); } } response.put("draw", HttpServletUtils.getIntegerParameter(request, "draw")); response.put("recordsTotal", numRecords); response.put("recordsFiltered", numRecords); response.put("data", rows); } catch (final Throwable e) { Logs.error(this, "Error executing query: " + query, e); response.put("error", "Error executing query"); } return response; } public ElementContainer newDetailView(final Object object, final List<KeySerializer> serializers) { final KeySerializerDetailSerializer model = new KeySerializerDetailSerializer(serializers); model.setObject(object); final DetailView detailView = new DetailView(model, this.typeName); return new ElementContainer(detailView); } /** * Construct a new form for the object using the specified list of fields keys. The * form is created without the form title. * * @param <T> The type of form to return. * @param object The object to create the form for. * @param keyListName The name of the list of keys for the fields to include * on the form. * @return The generated form. */ @SuppressWarnings("unchecked") public <F extends Form> F newForm(final Object object, final String keyListName) { final HtmlUiBuilderObjectForm form = newForm(object, getTypeName(), getKeyList(keyListName)); return (F)form; } @SuppressWarnings("unchecked") public <F extends Form> F newForm(final Object object, final String formName, final List<String> keys) { final HtmlUiBuilderObjectForm form = new HtmlUiBuilderObjectForm(object, this, formName, keys); return (F)form; } @SuppressWarnings("unchecked") public <F extends Form> F newForm(final Object object, final String formName, final String keyList) { final List<String> keys = getKeyList(keyList); return (F)newForm(object, formName, keys); } protected void newKeyList(final String name, final List<String> fieldNames) { this.keyLists.put(name, Lists.toArray(fieldNames)); } protected T newObject() { return null; } public Element newObjectAddPage(final Map<String, Object> defaultValues, final String prefix, final String preInsertMethod) throws IOException, ServletException { final T object = newObject(); final HttpServletRequest request = HttpServletUtils.getRequest(); Property.set(object, defaultValues); // if (!canAddObject(request)) { // response.sendError(HttpServletResponse.SC_FORBIDDEN, // "No permission to edit " + getTypeName() + " #" + getId()); // return null; // } final Map<String, Object> parameters = new HashMap<>(); final String pageName = getName(prefix, "add"); final Set<String> parameterNamesToSave = new HashSet<>(); final Form form = newTableForm(object, pageName); for (final String param : parameterNamesToSave) { form.addSavedParameter(param, request.getParameter(param)); } form.initialize(request); if (form.isPosted() && form.isMainFormTask()) { if (form.isValid()) { if ((Boolean)JavaBeanUtil.method(this, preInsertMethod, form, object)) { insertObject(object); parameters.put("message", "Saved"); final Object id = Property.get(object, getIdPropertyName()); parameters.put(getIdParameterName(), id); postInsert(object); final String viewName = getName(prefix, "view"); final String url = getPageUrl(viewName, parameters); redirectAfterCommit(url); return new TabElementContainer(); } } } final Page page = getPage(pageName); final String title = page.getExpandedTitle(); request.setAttribute("title", title); final Menu actionMenu = new Menu(); addMenuItem(actionMenu, prefix, "list", "Cancel", "_top").addProperty("buttonClass", "btn-danger"); addMenuItem(actionMenu, prefix, "add", "Clear Fields").addProperty("buttonClass", "btn-warning"); final String name = form.getName(); final Menu saveMenu = new Menu("Save", "javascript:$('#" + name + "').submit()"); saveMenu.addProperty("buttonClass", "btn-primary"); actionMenu.addMenuItem(saveMenu); final ButtonsToolbarElement buttonsToolbar = new ButtonsToolbarElement(actionMenu); final ElementContainer view = new ElementContainer(form, buttonsToolbar); final TabElementContainer tabs = new TabElementContainer(); tabs.add(title, view); return tabs; } public Element newObjectEditPage(final T object, final String prefix) throws IOException, ServletException { if (object == null) { throw new PageNotFoundException(); } else { final HttpServletRequest request = HttpServletUtils.getRequest(); final Set<String> parameterNamesToSave = new HashSet<>(); parameterNamesToSave.add(getIdParameterName()); final String pageName = getName(prefix, "edit"); final Form form = newTableForm(object, pageName); for (final String param : parameterNamesToSave) { form.addSavedParameter(param, request.getParameter(param)); } form.initialize(request); if (form.isPosted() && form.isMainFormTask()) { if (form.isValid() && preUpdate(form, object)) { updateObject(object); postUpdate(object); final Map<String, Object> parameters = new HashMap<>(); // Get after object has changed final Object id = Property.get(object, getIdPropertyName()); parameters.put(getIdParameterName(), id); final Page viewPage = getPage(prefix, "view"); final String url = viewPage.getFullUrl(parameters); redirectAfterCommit(url); return new TabElementContainer(); } else { setRollbackOnly(object); } } else { setRollbackOnly(object); } final Page page = getPage(prefix, "edit"); final String title = page.getExpandedTitle(); request.setAttribute("title", title); final Menu actionMenu = new Menu(); addMenuItem(actionMenu, prefix, "view", "Cancel", "_top").addProperty("buttonClass", "btn-danger"); addMenuItem(actionMenu, prefix, "edit", "Revert to Saved", "_top").addProperty("buttonClass", "btn-warning"); final String name = form.getName(); final Menu saveItem = new Menu("Save", "javascript:$('#" + name + "').submit()"); saveItem.addProperty("buttonClass", "btn-primary"); actionMenu.addMenuItem(saveItem); final ButtonsToolbarElement buttonsToolbar = new ButtonsToolbarElement(actionMenu); final ElementContainer view = new ElementContainer(form, buttonsToolbar); final TabElementContainer tabs = new TabElementContainer(); tabs.add(title, view); return tabs; } } @SuppressWarnings("unchecked") public <F extends Form> F newTableForm(final Object object, final String keyListName) { final List<String> keyList = getKeyList(keyListName); final UiBuilderObjectForm form = new UiBuilderObjectForm(object, this, getTypeName(), keyList); return (F)form; } protected void newView(final String name, final List<?> elements) { final List<KeySerializer> serializers = new ArrayList<>(); this.viewSerializers.put(name, serializers); for (final Object element : elements) { if (element != null) { KeySerializer serializer = null; if (element instanceof KeySerializer) { serializer = (KeySerializer)element; } else { final String key = element.toString(); serializer = this.keySerializers.get(key); if (serializer == null) { final String title = getLabel(key); serializer = new BuilderSerializer(key, title, this); } } if (serializer instanceof HtmlUiBuilderAware) { @SuppressWarnings("unchecked") final HtmlUiBuilderAware<HtmlUiBuilder<?>> builderAware = (HtmlUiBuilderAware<HtmlUiBuilder<?>>)serializer; builderAware.setHtmlUiBuilder(this); } serializers.add(serializer); } } } protected void newView(final String name, final Object... elements) { newView(name, Arrays.asList(elements)); } protected void notFound(final HttpServletResponse response, final String message) throws IOException { response.sendError(HttpServletResponse.SC_NOT_FOUND, message); } public void postInsert(final T object) { } public void postUpdate(final T object) { } public boolean preInsert(final Form form, final T object) { return true; } public boolean preUpdate(final Form form, final T object) { return true; } public void redirectAfterCommit(final String url) { final Map<String, Object> parameters = new HashMap<>(); final HttpServletRequest request = HttpServletUtils.getRequest(); for (final String parameterName : Arrays.asList("plain", "htmlCss")) { final String value = request.getParameter(parameterName); if (Property.hasValue(value)) { parameters.put(parameterName, value); } } final String urlWithParameters = UrlUtil.getUrl(url, parameters); Transaction.afterCommit(() -> HttpServletUtils.redirect(urlWithParameters)); } public Void redirectPage(final String pageName) { String url = getPageUrl(pageName); if (url == null) { url = ".."; } redirectAfterCommit(url); return null; } public Object redirectToTab(final Object parentBuilder, final String parentPageName, final String tabName) { final HtmlUiBuilder<?> builder = getBuilder(parentBuilder); if (builder != null) { final Page parentPage = builder.getPage(parentPageName); if (parentPage != null) { String url = parentPage.getFullUrl(); if (url != null) { url += "#" + getTypeName() + "_" + tabName; redirectAfterCommit(url); return null; } } } throw new RuntimeException( "Unable to get page " + parentPageName + " from builder " + parentBuilder); } public void referrerRedirect(final HttpServletRequest request) { final String url = request.getHeader("Referer"); redirectAfterCommit(url); } /** * Serialize the value represented by the key from the object. * * @param out The XML writer to serialize to. * @param object The object to get the value from. * @param key The key to serialize. * @throws IOException If there was an I/O error serializing the value. */ public void serialize(final XmlWriter out, final Object object, final String key) { if (object == null) { serializeNullLabel(out, key); } else { final Object serializer = this.keySerializers.get(key); if (serializer == null) { String path = null; String valueKey = key; final Matcher linkMatcher = LINK_KEY_PATTERN.matcher(key); if (linkMatcher.matches()) { path = linkMatcher.group(1); valueKey = linkMatcher.group(2); } HtmlUiBuilder<? extends Object> uiBuilder = this; final String[] parts = valueKey.split("\\."); Object currentObject = object; for (int i = 0; i < parts.length - 1; i++) { final String keyName = parts[i]; try { currentObject = getProperty(currentObject, keyName); if (currentObject == null) { serializeNullLabel(out, keyName); return; } uiBuilder = getBuilder(currentObject); } catch (final IllegalArgumentException e) { final String message = currentObject.getClass().getName() + " does not have a property " + keyName; this.log.error(e.getMessage(), e); out.element(HtmlElem.B, message); return; } } final String lastKey = parts[parts.length - 1]; if (path == null) { if (uiBuilder == this) { try { final Object value = getProperty(currentObject, lastKey); if (value == null) { serializeNullLabel(out, lastKey); return; } else { final TypeSerializer typeSerializer = this.classSerializers.get(value.getClass()); if (typeSerializer == null) { final String stringValue; if (value instanceof Collection) { final Collection<?> collection = (Collection<?>)value; stringValue = Strings.toString(", ", collection); } else { stringValue = value.toString(); } if (stringValue.length() > 0) { out.text(stringValue); return; } } else { typeSerializer.serialize(out, value); return; } } } catch (final IllegalArgumentException e) { final String message = currentObject.getClass().getName() + " does not have a property " + key; this.log.error(e.getMessage(), e); out.element(HtmlElem.B, message); return; } } else { uiBuilder.serialize(out, currentObject, lastKey); } } else { uiBuilder.serializeLink(out, currentObject, lastKey, path); } } else { if (serializer instanceof TypeSerializer) { final TypeSerializer typeSerializer = (TypeSerializer)serializer; typeSerializer.serialize(out, object); return; } else if (serializer instanceof KeySerializer) { final KeySerializer keySerializer = (KeySerializer)serializer; keySerializer.serialize(out, object); return; } } } } public void serializeLink(final XmlWriter out, final Object object, final String key, final String pageName) { final Map<String, Object> parameters = new HashMap<>(); final Object id = getIdValue(object); parameters.put(this.idParameterName, id); parameters.put(key, getProperty(object, key)); final String url = getPageUrl(pageName, parameters); if (url == null) { serializeNullLabel(out, pageName); } else { out.startTag(HtmlElem.A); out.attribute(HtmlAttr.HREF, url); out.attribute(HtmlAttr.TARGET, "_top"); serialize(out, object, key); out.endTag(HtmlElem.A); } } public void serializeLink(final XmlWriter out, final Object object, final String key, final String pageName, final Map<String, String> parameterKeys) { final Map<String, Object> parameters = new HashMap<>(); if (parameterKeys.isEmpty()) { final Object id = getIdValue(object); parameters.put(this.idParameterName, id); } else { for (final Entry<String, String> parameterKey : parameterKeys.entrySet()) { final String parameterName = parameterKey.getKey(); final String keyName = parameterKey.getValue(); final Object value = getProperty(object, keyName); if (value != null) { parameters.put(parameterName, value); } } } final String url = getPageUrl(pageName, parameters); if (url == null) { serialize(out, object, key); } else { out.startTag(HtmlElem.A); out.attribute(HtmlAttr.HREF, url); out.attribute(HtmlAttr.TARGET, "_top"); serialize(out, object, key); out.endTag(HtmlElem.A); } } /** * Serialize the message where a key has no value. The default is the * character '-'. * * @param out The XML writer to serialize to. * @param key The key to serialize the no value message for. * @throws IOException If there was an I/O error serializing the value. */ public void serializeNullLabel(final XmlWriter out, final String key) { final String nullLabel = getNullLabel(key); if (nullLabel == null) { final int dotIndex = key.lastIndexOf('.'); if (dotIndex == -1) { out.text("-"); } else { serializeNullLabel(out, key.substring(0, dotIndex)); } } else { out.text(nullLabel); } } @Override public void setBeanFactory(final BeanFactory beanFactory) { this.beanFactory = beanFactory; } /** * Set the factory used to get related HTML UI builders, * * @param builderFactory The factory. */ public void setBuilderFactory(final HtmlUiBuilderFactory builderFactory) { this.builderFactory = builderFactory; } public void setDefaultPageSize(final int defaultPageSize) { this.defaultPageSize = defaultPageSize; } /** * @param fieldInstructions The fieldInstructions to set. */ public void setFieldInstructions(final Map<String, String> fieldInstructions) { this.fieldInstructions = fieldInstructions; } public void setFields(final Map<String, Element> fields) { this.fields = fields; } /** * @param idParameterName The idParameterName to set. */ public void setIdParameterName(final String idParameterName) { this.idParameterName = idParameterName; } public void setIdPropertyName(final String idPropertyName) { this.idPropertyName = idPropertyName; } /** * Set the key list with the specified name. * * @param name The name of the key list. * @param keyList<String> The key list. */ public void setKeyList(final String name, final List<String> keyList) { this.keyLists.put(name, keyList); } /** * Set the map of key lists. * * @param keyLists The map of key lists. */ public void setKeyLists(final Map<String, List<String>> keyLists) { this.keyLists = keyLists; if (!keyLists.containsKey("list")) { setKeyList("list", getKeyList("listView")); } if (!keyLists.containsKey("detail")) { setKeyList("detail", getKeyList("detailView")); } if (!keyLists.containsKey("form")) { setKeyList("form", getKeyList("formView")); } } /** * @param labels The labels to set. */ public void setLabels(final Map<String, String> labels) { this.labels.putAll(labels); } public void setListSortOrder(final int columnIndex, final boolean ascending) { String direction; if (ascending) { direction = "asc"; } else { direction = "desc"; } final List<Object> columnSortOrder = Arrays.asList(columnIndex, direction); final List<List<Object>> sortOrder = Collections.singletonList(columnSortOrder); this.listSortOrder.put("list", sortOrder); } public void setListSortOrder(final Map<String, List<List<Object>>> listSortOrder) { this.listSortOrder.putAll(listSortOrder); } protected void setListSortOrder(final String pageName, final List<List<Object>> listSortOrder) { this.listSortOrder.put(pageName, listSortOrder); } public void setMaxPageSize(final int maxPageSize) { this.maxPageSize = maxPageSize; } public void setMessages(final Map<String, String> messages) { this.messages = messages; } /** * @param nullLabels The nullLabels to set. */ public void setNullLabels(final Map<String, String> nullLabels) { this.nullLabels = nullLabels; } public void setPages(final Collection<Page> pages) { for (final Page page : pages) { addPage(page); } } public void setPagesByName(final Map<String, Page> pagesByName) { this.pagesByName = pagesByName; } public String setPageTitle(final HttpServletRequest request, final String pageName) { final String title = getPageTitle(pageName); if (request.getAttribute("pageTitle") == null) { request.setAttribute("title", title); request.setAttribute("pageTitle", title); } return title; } public void setPageTitleAttribute(final HttpServletRequest request, final String pageName) { final Page page = getPage(pageName); if (page != null) { final String title = page.getExpandedTitle(); request.setAttribute("title", title); request.setAttribute("pageHeading", title); } } public void setPageUrls(final Map<String, String> pageUrls) { this.pageUrls = pageUrls; } public void setPluralTitle(final String pluralTitle) { this.pluralTitle = pluralTitle; } public void setRollbackOnly(final T object) { } public void setSerializers(final Collection<KeySerializer> keySerializers) { for (final KeySerializer serializer : keySerializers) { addKeySerializer(serializer); } } @Override public void setServletContext(final ServletContext servletContext) { } public void setTitle(final String typeLabel) { this.title = typeLabel; } public void setTypeName(final String typeName) { this.typeName = typeName; if (this.idParameterName == null) { this.idParameterName = typeName + "Id"; } } public void setUsePathVariables(final boolean usePathVariables) { this.usePathVariables = usePathVariables; } public void setValue(final Object object, final String key, final Object value) { if (object instanceof Record) { final Record record = (Record)object; record.setValueByPath(key, value); } else if (object instanceof Map) { @SuppressWarnings("unchecked") final Map<String, Object> map = (Map<String, Object>)object; map.put(key, value); } else { Property.setSimple(object, key, value); } } public void setViews(final Map<String, List<?>> views) { for (final Entry<String, List<?>> view : views.entrySet()) { final String name = view.getKey(); final List<?> elements = view.getValue(); newView(name, elements); } } protected void updateObject(final T object) { } public boolean validateForm(final HtmlUiBuilderObjectForm form) { return true; } public boolean validateForm(final UiBuilderObjectForm uiBuilderObjectForm) { return true; } }