/* * RHQ Management Platform * Copyright (C) 2005-2008 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also as published by the Free * Software Foundation. * * This program 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 and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.rhq.core.gui.util; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.UUID; import javax.el.ValueExpression; import javax.faces.application.Application; import javax.faces.component.UIComponent; import javax.faces.component.UIInput; import javax.faces.component.UIOutput; import javax.faces.component.UIParameter; import javax.faces.component.UIForm; import javax.faces.component.UIViewRoot; import javax.faces.component.html.HtmlColumn; import javax.faces.component.html.HtmlCommandButton; import javax.faces.component.html.HtmlCommandLink; import javax.faces.component.html.HtmlDataTable; import javax.faces.component.html.HtmlForm; import javax.faces.component.html.HtmlGraphicImage; import javax.faces.component.html.HtmlMessage; import javax.faces.component.html.HtmlOutputLabel; import javax.faces.component.html.HtmlOutputLink; import javax.faces.component.html.HtmlOutputText; import javax.faces.component.html.HtmlPanelGrid; import javax.faces.component.html.HtmlPanelGroup; import javax.faces.context.FacesContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.richfaces.component.html.HtmlSeparator; import org.richfaces.component.html.HtmlSimpleTogglePanel; /** * A set of utility methods for working with JSF {@link UIComponent}s. * * @author Ian Springer */ public abstract class FacesComponentUtility { public static final String NO_STYLE_CLASS = null; private static final String DISABLED_ATTRIBUTE_NAME = "disabled"; private static final String READONLY_ATTRIBUTE = "readonly"; private static final String UNSET_ATTRIBUTE = "unset"; private static final String OVERRIDE_ATTRIBUTE = "override"; @NotNull public static HtmlPanelGroup createBlockPanel(FacesComponentIdFactory idFactory, String styleClass) { HtmlPanelGroup panel = createComponent(HtmlPanelGroup.class, idFactory); panel.setLayout("block"); panel.setStyleClass(styleClass); return panel; } @NotNull public static HtmlPanelGroup addBlockPanel(@NotNull UIComponent parent, FacesComponentIdFactory idFactory, String styleClass) { HtmlPanelGroup panel = createBlockPanel(idFactory, styleClass); parent.getChildren().add(panel); return panel; } @NotNull public static HtmlPanelGroup addInlinePanel(@NotNull UIComponent parent, FacesComponentIdFactory idFactory, String styleClass) { HtmlPanelGroup panel = createComponent(HtmlPanelGroup.class, idFactory); panel.setStyleClass(styleClass); parent.getChildren().add(panel); return panel; } @NotNull public static HtmlSimpleTogglePanel addSimpleTogglePanel(@NotNull UIComponent parent, FacesComponentIdFactory idFactory, String label) { HtmlSimpleTogglePanel panel = createComponent(HtmlSimpleTogglePanel.class, idFactory); panel.setLabel(label); //panel.setAjaxSingle(true); panel.setSwitchType("client"); parent.getChildren().add(panel); return panel; } @NotNull public static HtmlSeparator addSeparator(@NotNull UIComponent parent, FacesComponentIdFactory idFactory) { HtmlSeparator separator = createComponent(HtmlSeparator.class, idFactory); parent.getChildren().add(separator); return separator; } @NotNull public static HtmlOutputText addOutputText(@NotNull UIComponent parent, FacesComponentIdFactory idFactory, CharSequence value, String styleClass) { HtmlOutputText text = createComponent(HtmlOutputText.class, idFactory); text.setValue(value); text.setStyleClass(styleClass); parent.getChildren().add(text); return text; } @NotNull public static HtmlOutputText createOutputText(FacesComponentIdFactory idFactory, CharSequence value, String styleClass) { HtmlOutputText text = createComponent(HtmlOutputText.class, idFactory); if (value != null) value = value.toString().trim(); text.setValue(value); text.setStyleClass(styleClass); return text; } @NotNull public static HtmlColumn addColumn(@NotNull UIComponent parent, FacesComponentIdFactory idFactory, CharSequence headerText, String headerStyle) { HtmlColumn column = createComponent(HtmlColumn.class, idFactory); HtmlOutputText header = new HtmlOutputText(); header.setValue(headerText); header.setStyleClass(headerStyle); column.setHeader(header); parent.getChildren().add(column); return column; } @NotNull public static HtmlOutputText addVerbatimText(@NotNull UIComponent parent, CharSequence html) { HtmlOutputText outputText = createComponent(HtmlOutputText.class, null); outputText.setEscape(false); outputText.setValue(html); parent.getChildren().add(outputText); return outputText; } @NotNull public static UIOutput addButton(UIComponent parent, String label, String styleClass) { return FacesComponentUtility.addVerbatimText(parent, "<button class=\"" + styleClass + "\">" + label + "</button>"); } @NotNull public static UIOutput addJavaScript(@NotNull UIComponent parent, FacesComponentIdFactory idFactory, @Nullable CharSequence src, @Nullable CharSequence script) { UIOutput output = createComponent(UIOutput.class, idFactory); output.getAttributes().put("escape", Boolean.FALSE); StringBuilder value = new StringBuilder(); value.append("\n<script language=\"JavaScript\" type=\"text/javascript\""); if (src != null) { value.append(" src=\"").append(src).append("\""); } value.append(">"); if (script != null) { value.append("\n").append(script).append("\n"); } value.append("</script>\n"); output.setValue(value); parent.getChildren().add(output); return output; } @NotNull public static <T extends UIComponent> List<T> getDescendantsOfType(UIComponent component, Class<T> componentClass) { List<T> results = new ArrayList<T>(); getDescendantsOfType(component, componentClass, results); return results; } @SuppressWarnings("unchecked") private static <T extends UIComponent> void getDescendantsOfType(UIComponent component, Class<T> componentClass, List<T> results) { for (int i = 0; i < component.getChildren().size(); i++) { UIComponent child = component.getChildren().get(i); if (componentClass.isAssignableFrom(child.getClass())) { results.add((T) child); } getDescendantsOfType(child, componentClass, results); } } @SuppressWarnings("unchecked") public static <T extends UIComponent> T getAncestorOfType(UIComponent component, Class<T> componentClass) { if (component == null) { return null; } if (componentClass.isAssignableFrom(component.getClass())) { return (T) component; } else { return getAncestorOfType(component.getParent(), componentClass); } } @NotNull public static HtmlOutputLabel addLabel(@NotNull UIComponent parent, FacesComponentIdFactory idFactory, UIInput associatedInput, String value, String styleClass) { HtmlOutputLabel label = createComponent(HtmlOutputLabel.class, idFactory); label.setFor(associatedInput.getId()); label.setValue(value); label.setStyleClass(styleClass); parent.getChildren().add(label); return label; } @NotNull public static HtmlGraphicImage addGraphicImage(@NotNull UIComponent parent, FacesComponentIdFactory idFactory, String url, String alt) { HtmlGraphicImage image = FacesComponentUtility.createComponent(HtmlGraphicImage.class, idFactory); image.setUrl(url); image.setAlt(alt); image.setTitle(alt); parent.getChildren().add(image); return image; } @NotNull public static HtmlOutputLink addOutputLink(@NotNull UIComponent parent, FacesComponentIdFactory idFactory, String value) { HtmlOutputLink link = FacesComponentUtility.createComponent(HtmlOutputLink.class, idFactory); link.setValue(value); parent.getChildren().add(link); return link; } @NotNull public static UIParameter addParameter(@NotNull UIComponent parent, FacesComponentIdFactory idFactory, String name, String value) { UIParameter parameter = createComponent(UIParameter.class, idFactory); parameter.setName(name); parameter.setValue(value); parent.getChildren().add(parameter); return parameter; } @NotNull public static HtmlForm addForm(@NotNull UIComponent parent, FacesComponentIdFactory idFactory) { HtmlForm form = createComponent(HtmlForm.class, idFactory); form.setPrependId(false); parent.getChildren().add(form); return form; } @NotNull public static HtmlCommandButton addCommandButton(@NotNull UIComponent parent, FacesComponentIdFactory idFactory, String value, String styleClass) { HtmlCommandButton button = createComponent(HtmlCommandButton.class, idFactory); button.setValue(value); button.setStyleClass(styleClass); parent.getChildren().add(button); return button; } @NotNull public static HtmlCommandLink addCommandLink(@NotNull UIComponent parent, FacesComponentIdFactory idFactory) { HtmlCommandLink link = FacesComponentUtility.createComponent(HtmlCommandLink.class, idFactory); parent.getChildren().add(link); return link; } @NotNull public static HtmlMessage addMessage(@NotNull UIComponent parent, FacesComponentIdFactory idFactory, String associatedComponentId, String styleClass) { HtmlMessage message = FacesComponentUtility.createComponent(HtmlMessage.class, idFactory); message.setFor(associatedComponentId); message.setShowDetail(true); message.setErrorClass(styleClass); message.setWarnClass(styleClass); message.setFatalClass(styleClass); message.setInfoClass(styleClass); parent.getChildren().add(message); return message; } @NotNull public static HtmlPanelGrid addPanelGrid(@NotNull UIComponent parent, FacesComponentIdFactory idFactory, int columns, String styleClass) { HtmlPanelGrid panelGrid = FacesComponentUtility.createComponent(HtmlPanelGrid.class, idFactory); panelGrid.setColumns(columns); panelGrid.setStyleClass(styleClass); parent.getChildren().add(panelGrid); return panelGrid; } @NotNull public static HtmlDataTable addDataTable(@NotNull UIComponent parent, FacesComponentIdFactory idFactory, String styleClass) { HtmlDataTable dataTable = FacesComponentUtility.createComponent(HtmlDataTable.class, idFactory); dataTable.setStyleClass(styleClass); parent.getChildren().add(dataTable); return dataTable; } @Nullable public static String getExpressionAttribute(@NotNull UIComponent component, @NotNull String attribName) { return getExpressionAttribute(component, attribName, String.class); } @Nullable public static <T> T getExpressionAttribute(@NotNull UIComponent component, @NotNull String attribName, @NotNull Class<T> expectedType) { ValueExpression valueExpression = component.getValueExpression(attribName); T attribValue = (valueExpression != null) ? FacesExpressionUtility.getValue(valueExpression, expectedType) : null; return attribValue; } @NotNull public static Map<String, String> getParameters(@NotNull UIComponent component) { // Use a LinkedHashMap, so the order of the parameters is maintained. Map<String, String> params = new LinkedHashMap<String, String>(); List<UIComponent> children = component.getChildren(); for (UIComponent child : children) { if (child instanceof UIParameter) { UIParameter param = (UIParameter) child; Object paramValue = param.getValue(); params.put(param.getName(), (paramValue != null) ? paramValue.toString() : null); } } return params; } /** * Creates a {@link UIComponent} of the specified type. * * @param componentClass the type of {@link UIComponent} to create * * @return a {@link UIComponent} of the specified type */ public static <T extends UIComponent> T createComponent(Class<T> componentClass) { return createComponent(componentClass, null); } /** * Creates a {@link UIComponent} of the specified type. * * @param componentClass the type of {@link UIComponent} to create * @param idFactory a factory to use to create a unique id for the new component * * @return a {@link UIComponent} of the specified type */ @SuppressWarnings("unchecked") public static <T extends UIComponent> T createComponent(Class<T> componentClass, @Nullable FacesComponentIdFactory idFactory) { Application application = FacesContext.getCurrentInstance().getApplication(); String componentType = getComponentType(componentClass); T component = (T) application.createComponent(componentType); if (idFactory == null) { idFactory = new DefaultFacesComponentIdFactory(); } component.setId(idFactory.createUniqueId()); return component; } /** * Sets the "disabled" attribute on the specified component to the provided value. NOTE: Not all components will * render this attribute (e.g. HtmlInputHidden does not render it). * * @param component a component * @param disabled the new value for the "disabled" attribute */ public static void setDisabled(UIComponent component, boolean disabled) { component.getAttributes().put(DISABLED_ATTRIBUTE_NAME, disabled); } /** * Gets the value of the "disabled" attribute on the specified component, or <code>false</code> if the component does * not expose the attribute. * * @param component a component * * @return the value of the "disabled" attribute on the specified component, or <code>false</code> if the component * does not expose the attribute */ public static boolean isDisabled(UIComponent component) { Boolean disabled = (Boolean) component.getAttributes().get(DISABLED_ATTRIBUTE_NAME); return (disabled != null) && disabled; } /** * Sets the "readonly" attribute on the specified component to the provided value. NOTE: Only certain components * will render this attribute (e.g. HtmlInputText, HtmlInputSecret, and HtmlInputTextarea render it). * * @param input an input component * @param readonly the new value for the "readonly" attribute */ public static void setReadonly(UIInput input, boolean readonly) { input.getAttributes().put(READONLY_ATTRIBUTE, readonly); } /** * Gets the value of the "readonly" attribute on the specified component, or <code>false</code> if the component does * not expose the attribute. * * @param component a component * * @return the value of the "readonly" attribute on the specified component, or <code>false</code> if the component * does not expose the attribute */ public static boolean isReadonly(UIComponent component) { Boolean readonly = (Boolean) component.getAttributes().get(READONLY_ATTRIBUTE); return (readonly != null) && readonly; } /** * Sets the "unset" attribute on the specified component to the provided value. NOTE: This is a RHQ-specific * attribute, which is only rendered for the Config component. * * @param input an input component * @param unset the new value for the "unset" attribute */ public static void setUnset(UIInput input, boolean unset) { input.getAttributes().put(UNSET_ATTRIBUTE, unset); } /** * Gets the value of the "unset" attribute on the specified component, or <code>false</code> if the component does * not expose the attribute. * * @param component a component * * @return the value of the "unset" attribute on the specified component, or <code>false</code> if the component does * not expose the attribute */ public static boolean isUnset(UIComponent component) { Boolean unset = (Boolean) component.getAttributes().get(UNSET_ATTRIBUTE); return (unset != null) && unset; } public static void setOverride(UIInput input, boolean override) { input.getAttributes().put(OVERRIDE_ATTRIBUTE, override); } public static boolean isOverride(UIComponent component) { Boolean override = (Boolean) component.getAttributes().get(OVERRIDE_ATTRIBUTE); return (override == null || override); } /** * Removes the specified component from its parent if it has one. * * @param component a JSF component */ public static void detachComponent(UIComponent component) { UIComponent parent = component.getParent(); if (parent != null) { Iterator<UIComponent> children = parent.getChildren().iterator(); while (children.hasNext()) { UIComponent child = children.next(); if ((child.getId() != null) && child.getId().equals(component.getId())) { children.remove(); // remove the last child returned by next() break; } } } } /** * Returns the enclosing UIForm component, if any, from the specified component's ancestry. * * @param component the component * * @return the enclosing UIForm component, if any, from the specified component's ancestry * * @throws IllegalArgumentException if more than one UIForm is found in the component's ancestry */ @Nullable public static UIForm getEnclosingForm(UIComponent component) throws IllegalArgumentException { UIForm ret = null; while (component != null) { if (component instanceof UIForm) { if (ret != null) { // Cannot have a doubly-nested form! throw new IllegalArgumentException(); } ret = (UIForm) component; } component = component.getParent(); } return ret; } private static <T extends UIComponent> String getComponentType(Class<T> componentClass) { String componentType; try { componentType = (String) componentClass.getDeclaredField("COMPONENT_TYPE").get(null); } catch (Exception e) { throw new IllegalStateException(e); } return componentType; } private static class DefaultFacesComponentIdFactory implements FacesComponentIdFactory { public String createUniqueId() { // NOTE: Our id's *must* begin with UIViewRoot.UNIQUE_ID_PREFIX ("j_id") to prevent HtmlOutputText // components from being rendered as SPAN elements // (see com.sun.faces.renderkit.html_basic.TextRenderer#getEndTextToRender()). return UIViewRoot.UNIQUE_ID_PREFIX + "_RHQ_" + UUID.randomUUID(); } } }