Java tutorial
/*************************************************************** * This file is part of the [fleXive](R) framework. * * Copyright (c) 1999-2014 * UCS - unique computing solutions gmbh (http://www.ucs.at) * All rights reserved * * The [fleXive](R) project is free software; you can redistribute * it and/or modify it under the terms of the GNU Lesser General Public * License version 2.1 or higher as published by the Free Software Foundation. * * The GNU Lesser General Public License can be found at * http://www.gnu.org/licenses/lgpl.html. * A copy is found in the textfile LGPL.txt and important notices to the * license from the author are found in LICENSE.txt distributed with * these libraries. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * For further information about UCS - unique computing solutions gmbh, * please see the company website: http://www.ucs.at * * For further information about [fleXive](R), please see the * project website: http://www.flexive.org * * * This copyright notice MUST APPEAR in all copies of the file! ***************************************************************/ package com.flexive.faces; import com.flexive.faces.beans.MessageBean; import com.flexive.faces.components.Thumbnail; import com.flexive.faces.messages.FxFacesMessage; import com.flexive.faces.messages.FxFacesMessages; import com.flexive.faces.model.FxJSFSelectItem; import com.flexive.shared.*; import com.flexive.shared.content.FxPK; import com.flexive.shared.exceptions.FxApplicationException; import com.flexive.shared.exceptions.FxNotFoundException; import com.flexive.shared.scripting.FxScriptInfo; import com.flexive.shared.search.FxPaths; import com.flexive.shared.search.FxResultSet; import com.flexive.shared.security.Account; import com.flexive.shared.structure.FxSelectList; import com.flexive.shared.structure.FxSelectListItem; import com.flexive.shared.value.BinaryDescriptor; import com.flexive.shared.value.FxBinary; import com.flexive.shared.value.FxBoolean; import com.flexive.shared.value.FxValue; import com.flexive.shared.value.renderer.FxValueRendererFactory; import com.flexive.war.FxRequest; import com.flexive.war.filter.FxResponseWrapper; import com.flexive.war.servlet.ThumbnailServlet; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.el.ValueExpression; import javax.faces.application.Application; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.model.SelectItem; import javax.faces.model.SelectItemGroup; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponseWrapper; import javax.servlet.http.HttpSession; import java.text.Collator; import java.util.*; import static javax.faces.context.FacesContext.getCurrentInstance; /** * Utility class for JSF functionality within beans. * * @author Gregor Schober (gregor.schober@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at) * @author Daniel Lichtenberger (daniel.lichtenberger@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at) * @version $Rev$ */ public class FxJsfUtils { private static final Log LOG = LogFactory.getLog(FxJsfUtils.class); private static final String JSON_RPC_SERVLET = "/adm/JSON-RPC"; private final static String BRACKET_OPEN = "__-"; private final static String BRACKET_CLOSE = "_-_"; private final static String SLASH = "-__"; /** * Encode identifier to be valid for JSF components * * @param identifier id to encode * @return encoded id */ public static String encodeJSFIdentifier(String identifier) { // We may only use '-' and '_' characters to encode JSF identifiers identifier = identifier.replaceAll("/", SLASH); identifier = identifier.replaceAll("\\[", BRACKET_OPEN); identifier = identifier.replaceAll("]", BRACKET_CLOSE); return identifier; } /** * Decode a JSF identifier created by encodeJSFIdentifier * * @param identifier id to decode * @return decoded id */ public static String decodeJSFIdentifier(String identifier) { return identifier.replaceAll(BRACKET_CLOSE, "]").replace(SLASH, "/").replace(BRACKET_OPEN, "["); } /** * Formats the value as returned from a flexive search query. * * @param propertyName the (optional) property name for the given value * @param value the value to be formatted * @param linkFormatter link formatter * @param linkFormat link format * @param itemLinkFormat item link format * @return the formatted string value * @since 3.1 */ public static String formatResultValue(String propertyName, Object value, ContentLinkFormatter linkFormatter, String linkFormat, String itemLinkFormat) { linkFormatter = linkFormatter != null ? linkFormatter : ContentLinkFormatter.getInstance(); if (value == null || (value instanceof FxValue && ((FxValue) value).isEmpty())) { return "<i>" + FxSharedUtils.getEmptyResultMessage() + "</i>"; } else if (value instanceof FxBinary) { final FxBinary binary = (FxBinary) value; return "<img src=\"" + FxContext.get().getContextPath() + ThumbnailServlet.getLink(XPathElement.getPK(binary.getXPath()), BinaryDescriptor.PreviewSizes.PREVIEW2, binary.getXPath(), binary.getBestTranslation().getCreationTime(), FxContext.getUserTicket().getLanguage()) + "\" alt=\"" + FxFormatUtils.escapeForJavaScript(binary.getBestTranslation().getName()) + "\" class=\"" + Thumbnail.CSS_CLASS + "\"/>"; } else if (value instanceof FxBoolean) { return MessageBean.getInstance() .getMessage("shared.result.value." + ((FxBoolean) value).getBestTranslation()); } else if (value instanceof FxValue) { return FxValueRendererFactory.getInstance().format((FxValue) value); } else if (value instanceof FxPK) { return linkFormatter.format(linkFormat, (FxPK) value); } else if (value instanceof FxPaths) { return linkFormatter.format(itemLinkFormat, (FxPaths) value); } else if (value instanceof FxResultSet.WrappedLock) { final FxResultSet.WrappedLock wrapped = (FxResultSet.WrappedLock) value; return FxSharedUtils.getMessage(FxSharedUtils.SHARED_BUNDLE, "shared.result.lockInfo.user", wrapped.getUsername(), wrapped.getLock().getLockType().getLabel()).toString(); } else { return value.toString(); // unsupported type } } private static String resolveUserId(long accountId) { final String contextKey = FxJsfUtils.class.getName() + "_accounts"; // fetch all accounts, cache in context scope if (FxContext.get().getAttribute(contextKey) == null) { try { FxContext.get().setAttribute(contextKey, EJBLookup.getAccountEngine().loadAll()); } catch (FxApplicationException e) { throw e.asRuntimeException(); } } @SuppressWarnings({ "unchecked" }) final List<Account> accounts = (List<Account>) FxContext.get().getAttribute(contextKey); for (Account account : accounts) { if (account.getId() == accountId) { return account.getName(); } } // could not resolve user ID - should not happen return String.valueOf(accountId); } /** * Private constructor to avoid instantiation */ private FxJsfUtils() { } /** * Get managed beans based on the beans name. * * @param beanName the beans name * @return the managed beans associated with the beans name */ public static Object getManagedBean(String beanName) { return getValueExpression(getJsfEl(beanName)).getValue(getCurrentInstance().getELContext()); } /** * Returns the managed beans of the given class. Works only for flexive beans that * have the full class name and a "fx" prefix by convention. For example, * the ContentEditorBean is registered with JSF as "fxContentEditorBean". * * @param beanClass the beans class * @return the managed beans of the given class, or null if none exists */ public static <T> T getManagedBean(Class<T> beanClass) { return beanClass.cast(getManagedBean("fx" + beanClass.getSimpleName())); } /** * Remove the managed beans based on the beans name. * * @param beanName the beans name of the managed beans to be removed */ public static void resetManagedBean(String beanName) { getValueExpression(getJsfEl(beanName)).setValue(getCurrentInstance().getELContext(), null); } /** * Replace the managed beans based on the beans name. * * @param beanName the beans name of the managed beans to be replaced * @param bean the new beans */ public static void replaceManagedBean(String beanName, Object bean) { getValueExpression(getJsfEl(beanName)).setValue(getCurrentInstance().getELContext(), bean); } /** * Store the managed beans inside the session scope. * * @param beanName the name of the managed beans to be stored * @param managedBean the managed beans to be stored */ public static void setManagedBeanInSession(String beanName, Object managedBean) { //noinspection unchecked Map<String, Object> map = getCurrentInstance().getExternalContext().getSessionMap(); map.put(beanName, managedBean); } /** * Get parameter value from request scope. * * @param name the name of the parameter * @return the parameter value */ public static String getRequestParameter(String name) { return getCurrentInstance().getExternalContext().getRequestParameterMap().get(name); } /** * Evaluate the value of a JSF expression. * * @param el the JSF expression * @return the integer value associated with the JSF expression */ public static Object evalObject(String el) { if (el == null) { return null; } return isValueReference(el) ? getElValue(el) : el; } /** * <a href="http://issues.apache.org/struts/browse/SHALE-305">Source: SHALE-305.</a><br/> * Return true if the specified string contains an EL expression. * <p/> * This is taken almost verbatim from javax.faces.webapp.UIComponentTag * in order to remove JSP dependencies from the renderers. * * @param value the expression to be checked * @return true if the given value contains an EL expression */ public static boolean isValueReference(String value) { if (value == null) return false; int start = value.indexOf("#{"); if (start < 0) return false; int end = value.lastIndexOf('}'); return (end >= 0 && start < end); } /** * Evaluate the integer value of a JSF expression. * * @param el the JSF expression * @return the integer value associated with the JSF expression */ public static Integer evalInt(String el) { if (el == null) { return null; } Object value = evalObject(el); if (value instanceof Integer) { return (Integer) value; } else if (value != null) { return new Integer(value.toString()); } else { return null; } } /** * Evaluate the string value of a JSF expression. * * @param el the JSF expression * @return the string value associated with the JSF expression */ public static String evalString(String el) { if (el == null) { return null; } Object value = evalObject(el); return value != null ? value.toString() : null; } public static Application getApplication() { return getCurrentInstance().getApplication(); } private static ValueExpression getValueExpression(String el) { return getApplication().getExpressionFactory().createValueExpression(getCurrentInstance().getELContext(), el, Object.class); } private static Object getElValue(String el) { return getValueExpression(el).getValue(getCurrentInstance().getELContext()); } private static String getJsfEl(String value) { return "#{" + value + "}"; } /** * Gets the id of the given parameter name. * * @param parameterName the parameter name * @return the id */ public static long getId(String parameterName) { String valueText = null; try { valueText = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap() .get(parameterName); return Long.parseLong(valueText); } catch (NumberFormatException e) { throw new IllegalArgumentException( "Couldn't parse '" + parameterName + "'='" + valueText + "' as a long"); } } public static void setSessionAttribute(String key, Object value) { //noinspection unchecked FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(key, value); } public static Object getSessionAttribute(String key) { return FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(key); } public static void removeSessionAttribute(String key) { FacesContext.getCurrentInstance().getExternalContext().getSessionMap().remove(key); } public static String getParameter(String name) { return FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get(name); } public static long getLongParameter(String name, long defaultValue) { return Long.valueOf(StringUtils.defaultString(getParameter(name), String.valueOf(defaultValue))); } public static boolean hasParameter(String name) { String s = getParameter(name); return s != null && s.length() > 0; } public static long getLongParameter(String name) { return Long.valueOf(getParameter(name)); } public static int getIntParameter(String name, int defaultValue) { return Integer.valueOf(StringUtils.defaultString(getParameter(name), String.valueOf(defaultValue))); } public static int getIntParameter(String name) { return Integer.valueOf(getParameter(name)); } public static boolean getBooleanParameter(String name, boolean defaultValue) { return Boolean.valueOf(StringUtils.defaultString(getParameter(name), String.valueOf(defaultValue))); } public static boolean getBooleanParameter(String name) { return Boolean.valueOf(getParameter(name)); } /** * Returns the servlet context. * <p/> * The result is null if no faces context or external context is available. * * @return the servlet context */ public static ServletContext getServletContext() { try { return (ServletContext) getCurrentInstance().getExternalContext().getContext(); } catch (Throwable t) { return null; } } /** * Gets the session from the faces context. * <p/> * The result is null if no faces context is available. * * @return the session from the faces context */ public static HttpSession getSession() { try { return (HttpSession) getCurrentInstance().getExternalContext().getSession(false); } catch (Throwable t) { return null; } } /** * Gets the request from the faces context. * <p/> * The result is null if no faces context is available. * * @return the request from the faces context */ public static FxRequest getRequest() { try { HttpServletRequestWrapper wrapper = (HttpServletRequestWrapper) getCurrentInstance() .getExternalContext().getRequest(); while (wrapper != null && !(wrapper instanceof FxRequest)) { wrapper = (HttpServletRequestWrapper) wrapper.getRequest(); } return (FxRequest) wrapper; } catch (Throwable t) { return null; } } /** * Gets the response from the faces context. * <p/> * The result is null if no faces context is available. * * @return the response from the faces context */ public static FxResponseWrapper getResponse() { HttpServletResponseWrapper resp = (HttpServletResponseWrapper) getCurrentInstance().getExternalContext() .getResponse(); FxResponseWrapper fresp; if (resp == null) { fresp = null; } else if (resp instanceof FxResponseWrapper) { fresp = (FxResponseWrapper) resp; } else { fresp = (FxResponseWrapper) resp.getResponse(); } return fresp; } /** * Returns the URI of the JSON/RPC servlet * * @return the URI of the JSON/RPC servlet */ public static String getJsonServletUri() { return getRequest().getContextPath() + JSON_RPC_SERVLET; } /** * Get the server URL like "http://www.flexive.org" without the context path * * @return server URL */ public static String getServerURL() { final FxRequest req = FxJsfUtils.getRequest(); try { return req.getRequestURL().substring(0, req.getRequestURL().indexOf(req.getContextPath())); } catch (Exception e) { final HttpServletRequest r = req.getRequest(); return r.getProtocol() + "://" + r.getRemoteHost() + (r.getProtocol().startsWith("http") ? "" : ":" + r.getRemotePort()); } } /** * Return the localized message for the given key and replace all parameters with the given args. * * @param messageKey key of the message to be translated * @param args optional arguments to be replaced in the message * @return the localized message */ public static String getLocalizedMessage(String messageKey, Object... args) { return MessageBean.getInstance().getMessage(messageKey, args); } public static void addMessage(FacesMessage message) { getCurrentInstance().addMessage(null, message); } /** * Clears all faces messages (optional: for the given client id). * * @param clientId the client id to work on, or null to remove all messages. */ @SuppressWarnings("unchecked") public static void clearAllMessages(String clientId) { //noinspection unchecked Iterator<FacesMessage> it = (clientId == null || clientId.length() == 0) ? getCurrentInstance().getMessages() : getCurrentInstance().getMessages(clientId); while (it.hasNext()) { it.next(); it.remove(); } } /** * Gets all faces messages (optional: for the given client id). * * @param clientId the client id to work on, or null to remove all messages. * @return a array holding all messages */ @SuppressWarnings("unchecked") public static ArrayList<FacesMessage> getMessages(String clientId) { ArrayList<FacesMessage> result = new ArrayList<FacesMessage>(25); try { //noinspection unchecked Iterator<FacesMessage> it = (clientId == null || clientId.length() == 0) ? getCurrentInstance().getMessages() : getCurrentInstance().getMessages(clientId); while (it.hasNext()) { result.add(it.next()); } } catch (Throwable t) { result.add(new FacesMessage("Failed to build message list: " + t.getMessage())); } result.trimToSize(); return result; } /** * Gets all faces messages wrapped as FxFacesMessage (which gives access to the clientId) * * @return a array holding all messages */ @SuppressWarnings("unchecked") public static ArrayList<FxFacesMessage> getFxMessages() { final FacesContext ctx = getCurrentInstance(); // Get all messages belonging to an specific client id Iterator it = ctx.getClientIdsWithMessages(); ArrayList<FxFacesMessage> result = new ArrayList<FxFacesMessage>(5); while (it.hasNext()) { String clientId = String.valueOf(it.next()); //noinspection unchecked Iterator<FacesMessage> msgs = ctx.getMessages(clientId); while (msgs.hasNext()) { FacesMessage msg = msgs.next(); if (!(msg instanceof FxFacesMessage)) { msg = new FxFacesMessage(msg, clientId); } result.add((FxFacesMessage) msg); } } // get all messages that are not linked to an client it = ctx.getMessages(); while (it.hasNext()) { FacesMessage msg = (FacesMessage) it.next(); // Add the message, but only if it doesnt exist already with a filled in form/client id if (!messageExists(result, msg)) { if (!(msg instanceof FxFacesMessage)) { msg = new FxFacesMessage(msg, null); } result.add((FxFacesMessage) msg); } } // Return the result return result; } private static boolean messageExists(ArrayList<FxFacesMessage> list, FacesMessage msg) { for (FxFacesMessage comp : list) { if (comp.getSeverity() == msg.getSeverity() && comp.getSummary().equals(msg.getSummary()) && comp.getDetail().equals(msg.getDetail())) return true; } return false; } /** * Gets all faces messages grouped by a equal summary and wrapped as * FxFacesMessage (which gives access to the clientId). In case of a grouped message * the details of the original FacesMessage can be retrieved by calling getDetails() of * the FxFacesMessage. * * @return a array holding all (grouped) messages */ public static List<FxFacesMessages> getGroupedFxMessages() { ArrayList<FxFacesMessage> ffm = getFxMessages(); Hashtable<String, FxFacesMessages> grouped = new Hashtable<String, FxFacesMessages>(ffm.size()); for (FxFacesMessage msg : ffm) { String key = msg.getSeverity() + ":" + msg.getSummary(); FxFacesMessages exists = grouped.get(key); if (exists != null) { exists.addMessage(msg); } else { grouped.put(key, new FxFacesMessages(msg.getSummary(), msg.getSeverity(), msg)); } } return new ArrayList<FxFacesMessages>(grouped.values()); } /** * Find a parent of the given class. Throws a runtime exception if none is found. * * @param component the (child) component that searches an ancestor * @param cls the class or interface to be searched for in the component's ancestors * @return the parent component */ public static <T> T findAncestor(UIComponent component, Class<T> cls) { UIComponent current = component; if (current != null) { current = current.getParent(); } while (current != null && !(cls.isAssignableFrom(current.getClass()))) { current = current.getParent(); } if (current == null) { throw new FxNotFoundException(LOG, "ex.jsf.ancestor.notFound").asRuntimeException(); } return cls.cast(current); } /** * Create a new component of the given type. * * @param componentType the component type, e.g. <code>javax.faces.SelectOne</code> * @return the created component */ public static UIComponent createComponent(String componentType) { return getApplication().createComponent(componentType); } /** * Add a child component of the given type to the parent component. The child component * is returned to the caller. * * @param parent the parent component * @param id the component ID. If null, no ID will be set - in this case, * don't set an ID on the component afterwards, otherwise the wrong ID has already been published * in JSF2 events. * @param componentType child component type, e.g. <code>javax.faces.SelectOne</code> * @return the created child component * @since 3.1.3 */ public static UIComponent addChildComponent(UIComponent parent, String id, String componentType) { return addChildComponent(parent, id, componentType, false); } /** * Add a child component of the given type to the parent component. The child component * is returned to the caller. * * @param parent the parent component * @param id the component ID. If null, no ID will be set - in this case, * don't set an ID on the component afterwards, otherwise the wrong ID has already been published * in JSF2 events. * @param componentType child component type, e.g. <code>javax.faces.SelectOne</code> * @param isTransient if the component should be marked as transient * @return the created child component * @since 3.1.4 */ public static UIComponent addChildComponent(UIComponent parent, String id, String componentType, boolean isTransient) { if (LOG.isDebugEnabled()) { LOG.debug("Adding a component of type " + componentType + " to " + parent.getClientId(FacesContext.getCurrentInstance())); } final UIComponent child = createComponent(componentType); if (StringUtils.isNotBlank(id)) { child.setId(id); } child.setTransient(isTransient); parent.getChildren().add(child); return child; } /** * Return true if the current request has been submitted via Ajax4JSF. * * @return true if the current request has been submitted via Ajax4JSF. */ public static boolean isAjaxRequest() { return getRequest().getParameterMap().containsKey("AJAXREQUEST"); // TODO: JSF2 } /** * Recursively search for the first child of the given class type for the component. * * @param component the component to be searched * @param childType the required child type * @return the first child of the given type, or null if no child was found. */ public static <T extends UIComponent> T findChild(UIComponent component, Class<T> childType) { for (Object item : component.getChildren()) { final UIComponent child = (UIComponent) item; if (childType.isAssignableFrom(child.getClass())) { return childType.cast(child); } // search in this child's children final T nestedChild = findChild(child, childType); if (nestedChild != null) { return nestedChild; } } return null; } /** * Convert a list of SelectableObjects to JSF SelectItems. * * @param items the items to be converted * @param addEmptyElement if set to true a empty element is added * @return the given list converted to JSF SelectItems */ public static List<SelectItem> asSelectListWithName(List<? extends SelectableObjectWithName> items, boolean addEmptyElement) { final List<SelectItem> result = new ArrayList<SelectItem>(items.size() + (addEmptyElement ? 1 : 0)); if (addEmptyElement) result.add(new FxJSFSelectItem()); for (SelectableObjectWithName item : items) result.add(new FxJSFSelectItem(item)); return result; } /** * Convert a list of SelectableObjectWithLabels to JSF SelectItems. * * @param items the items to be converted * @param addEmptyElement if set to true a empty element is added * @return the given list converted to JSF SelectItems */ public static List<SelectItem> asSelectListWithLabel(List<? extends SelectableObjectWithLabel> items, boolean addEmptyElement) { final List<SelectItem> result = new ArrayList<SelectItem>(items.size() + (addEmptyElement ? 1 : 0)); if (addEmptyElement) result.add(new FxJSFSelectItem()); for (SelectableObjectWithLabel item : items) { // use the SelectableObject constructor to allow a fallback if the label is not set result.add(new FxJSFSelectItem((SelectableObject) item)); } return result; } /** * Convert the values of an enum to a select list. * * @param values the enum values to be converted * @return the select list backing the given enumeration values. */ public static <T extends Enum> List<SelectItem> enumsAsSelectList(T[] values) { final ArrayList<SelectItem> result = new ArrayList<SelectItem>(values.length); for (Enum value : values) result.add(new FxJSFSelectItem(value)); return result; } /** * Convert a list of SelectableObjectsWithLabel to JSF SelectItems. * * @param items the items to be converted * @return the given list converted to JSF SelectItems */ public static List<SelectItem> asSelectListWithLabel(List<? extends SelectableObjectWithLabel> items) { return asSelectListWithLabel(items, false); } /** * Convert a list of SelectableObjectsWithName to JSF SelectItems. * * @param items the items to be converted * @return the given list converted to JSF SelectItems */ public static List<SelectItem> asSelectListWithName(List<? extends SelectableObjectWithName> items) { return asSelectListWithName(items, false); } /** * Converts the given flexive select list to a list of JSF SelectItems. * * @param list the select list to be converted * @return a JSF select list corresponding to the given list options */ public static List<SelectItem> asSelectList(FxSelectList list) { final List<SelectItem> result = new ArrayList<SelectItem>(list.getItemCount()); for (FxSelectListItem item : (list.isSortEntries() ? list.getItemsSortedByLabel() : list.getItems())) result.add(new FxJSFSelectItem(item)); return result; } /** * Converts a list of String arrays (2 dim, containing value and display) to a list of JSF SelectItems. * * @param list the list of String arrays (2 dim, containing value and display) * @return a JSF select list corresponding to the given list options */ public static List<SelectItem> asSelectList(List<String[]> list) { final List<SelectItem> result = new ArrayList<SelectItem>(list.size()); for (String[] item : list) { if (item == null || item.length != 2) continue; result.add(new FxJSFSelectItem(item[0], item[1])); } Collections.sort(result, new SelectItemSorter()); return result; } /** * Sort a list of select items, including items of nested select item groups. * * @param items the list of items to be sorted * @param comparator the comparator to be used * @see SelectItemSorter * @since 3.2.0 */ public static void sortSelectItems(List<SelectItem> items, Comparator<SelectItem> comparator) { Collections.sort(items, comparator); for (SelectItem item : items) { if (item instanceof SelectItemGroup) { final SelectItem[] groupItems = ((SelectItemGroup) item).getSelectItems(); if (groupItems != null) { Collections.sort(Arrays.asList(groupItems), comparator); } } } } /** * Sort a list of select items, including items of nested select item groups. * * @param items the list of items to be sorted * @see SelectItemSorter * @since 3.2.0 */ public static void sortSelectItems(List<SelectItem> items) { sortSelectItems(items, new SelectItemSorter()); } /** * Comparator for sorting select items by their display label. * * @see #sortSelectItems(java.util.List) */ public static class SelectItemSorter implements Comparator<SelectItem> { private final Collator collator; public SelectItemSorter() { this.collator = FxSharedUtils.getCollator(); } @Override public int compare(SelectItem o1, SelectItem o2) { if (o1.getLabel() == null && o2.getLabel() != null) { return -1; } else if (o2.getLabel() == null && o1.getLabel() != null) { return 1; } else if (o1.getLabel() == null && o2.getLabel() == null) { return 0; } else { return this.collator.compare(o1.getLabel(), o2.getLabel()); } } } /** * Comparator for sorting FxScriptInfo objects by their name. */ public static class ScriptInfoSorter implements Comparator<FxScriptInfo> { private final Collator collator; public ScriptInfoSorter() { this.collator = FxSharedUtils.getCollator(); } @Override public int compare(FxScriptInfo o1, FxScriptInfo o2) { return this.collator.compare(o1.getName(), o2.getName()); } } /** * Checks if the minimum and maximum values of a muliplicity are in valid ranges * * @param min minMultiplicity * @param max maxMultiplicity * @throws FxApplicationException on errors */ public static void checkMultiplicity(int min, int max) throws FxApplicationException { if (min < 0) throw new FxApplicationException("ex.structure.multiplicity.minimum.invalid", min, max); if (max < 1) throw new FxApplicationException("ex.structure.multiplicity.maximum.invalid", max, min); if (min > max) throw new FxApplicationException("ex.structure.multiplicity.minimum.invalid", min, max); } /** * Returns the given component in the current view, or null if it does not exist. * * @param context the current faces context * @param clientId the component's client ID * @return the given component in the current view, or null if it does not exist. */ public static UIComponent findComponent(FacesContext context, String clientId) { return context.getViewRoot().findComponent(clientId); } /** * Returns true if the current request is served for the given browser, with at least the given * browser version. * * @param browser the desired browser * @param minVersion the minimum version. If set to -1, it is not checked. * @return true if the current browser matches the requested parameters * @since 3.1 */ public static boolean isBrowser(FxRequest.Browser browser, double minVersion) { return browser.equals(getRequest().getBrowser()) && getRequest().getBrowserVersion() >= minVersion; } /** * Returns true if the current request is served for the given browser, but the version does * not meet the given minimum version. * * @param browser the desired browser * @param minVersion the minimum version. If set to -1, it is not checked. * @return true if the current browser is served by the given browser, but the version is too old * @since 3.1 */ public static boolean isOlderBrowserThan(FxRequest.Browser browser, double minVersion) { return browser.equals(getRequest().getBrowser()) && getRequest().getBrowserVersion() < minVersion; } /** * Strip the context path from the given URL. The input is not validated. * * @param urlWithContextPath the URL with context path * @return the URL without context path */ public static String stripContextPath(String urlWithContextPath) { return urlWithContextPath == null ? null : urlWithContextPath.substring(getRequest().getContextPath().length()); } }