blob: cbf68ba7fdceb9bf7843f8a8c03ed45783dfefec [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.myfaces.tobago.internal.util;
import org.apache.myfaces.tobago.component.Attributes;
import org.apache.myfaces.tobago.component.RendererTypes;
import org.apache.myfaces.tobago.component.Tags;
import org.apache.myfaces.tobago.component.Visual;
import org.apache.myfaces.tobago.context.Markup;
import org.apache.myfaces.tobago.context.TobagoContext;
import org.apache.myfaces.tobago.internal.component.AbstractUIStyle;
import org.apache.myfaces.tobago.renderkit.LabelWithAccessKey;
import org.apache.myfaces.tobago.renderkit.css.Icons;
import org.apache.myfaces.tobago.renderkit.css.TobagoClass;
import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
import org.apache.myfaces.tobago.util.ComponentUtils;
import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.el.ELContext;
import javax.el.ValueExpression;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import javax.faces.model.SelectItemGroup;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
public final class HtmlRendererUtils {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private static final String FOCUS_KEY = HtmlRendererUtils.class.getName() + ".FocusId";
private HtmlRendererUtils() {
// to prevent instantiation
}
public static void renderFocus(
final String clientId, final boolean focus, final boolean error, final FacesContext facesContext,
final TobagoResponseWriter writer) throws IOException {
final Map<String, Object> requestMap = facesContext.getExternalContext().getRequestMap();
if (!requestMap.containsKey(FOCUS_KEY)
&& (clientId.equals(TobagoContext.getInstance(facesContext).getFocusId()) || focus || error)) {
requestMap.put(FOCUS_KEY, Boolean.TRUE);
writer.writeAttribute(HtmlAttributes.AUTOFOCUS, true);
}
}
public static String getRendererName(final FacesContext facesContext, final UIComponent component) {
final String rendererType = component.getRendererType();
return rendererType.substring(0, 1).toLowerCase(Locale.ENGLISH) + rendererType.substring(1);
}
public static void writeLabelWithAccessKey(final TobagoResponseWriter writer, final LabelWithAccessKey label)
throws IOException {
final int pos = label.getPos();
final String text = label.getLabel();
if (text != null) {
if (pos == -1) {
writer.writeText(text);
} else {
writer.writeText(text.substring(0, pos));
writer.startElement(HtmlElements.U);
writer.writeText(Character.toString(text.charAt(pos)));
writer.endElement(HtmlElements.U);
writer.writeText(text.substring(pos + 1));
}
}
}
public static void encodeIconOrImage(final TobagoResponseWriter writer, final String image) throws IOException {
if (image != null) {
if (image.startsWith("fa-")) {
writer.startElement(HtmlElements.I);
writer.writeClassAttribute(Icons.FA, Icons.custom(image));
writer.endElement(HtmlElements.I);
} else {
writer.startElement(HtmlElements.IMG);
writer.writeAttribute(HtmlAttributes.SRC, image, true);
writer.writeAttribute(HtmlAttributes.ALT, "", false);
writer.endElement(HtmlElements.IMG);
}
}
}
public static String getTitleFromTipAndMessages(final FacesContext facesContext, final UIComponent component) {
final String messages = ComponentUtils.getFacesMessageAsString(facesContext, component);
return HtmlRendererUtils.addTip(messages, ComponentUtils.getAttribute(component, Attributes.tip));
}
public static String addTip(final String title, final Object tip) {
String result = title;
if (tip != null) {
if (result != null && result.length() > 0) {
result += " :: ";
} else {
result = "";
}
result += tip;
}
return result;
}
public static void renderSelectItems(final UIInput component, final TobagoClass optionClass,
final Iterable<SelectItem> items, final Object[] values, final String[] submittedValues,
final TobagoResponseWriter writer, final FacesContext facesContext) throws IOException {
renderSelectItems(component, optionClass, items, values, submittedValues, null, writer, facesContext);
}
public static void renderSelectItems(final UIInput component, final TobagoClass optionClass,
final Iterable<SelectItem> items, final Object value, final String submittedValue,
final TobagoResponseWriter writer, final FacesContext facesContext) throws IOException {
renderSelectItems(component, optionClass, items, value != null ? new Object[]{value} : null,
submittedValue != null ? new String[]{submittedValue} : null, null, writer, facesContext);
}
public static void renderSelectItems(final UIInput component, final TobagoClass optionClass,
final Iterable<SelectItem> items, final Object[] values, final String[] submittedValues,
final Boolean onlySelected, final TobagoResponseWriter writer, final FacesContext facesContext)
throws IOException {
if (LOG.isDebugEnabled()) {
LOG.debug("component id = '{}'", component.getId());
LOG.debug("values = '{}'", Arrays.toString(values));
LOG.debug("submittedValues = '{}'", Arrays.toString(submittedValues));
}
for (final SelectItem item : items) {
if (item instanceof SelectItemGroup) {
writer.startElement(HtmlElements.OPTGROUP);
writer.writeAttribute(HtmlAttributes.LABEL, item.getLabel(), true);
if (item.isDisabled()) {
writer.writeAttribute(HtmlAttributes.DISABLED, true);
}
final SelectItem[] selectItems = ((SelectItemGroup) item).getSelectItems();
renderSelectItems(component, optionClass, Arrays.asList(selectItems), values, submittedValues,
onlySelected, writer, facesContext);
writer.endElement(HtmlElements.OPTGROUP);
} else {
Object itemValue = item.getValue();
// when using selectItem tag with a literal value: use the converted value
if (itemValue instanceof String && values != null && values.length > 0 && !(values[0] instanceof String)) {
itemValue = ComponentUtils.getConvertedValue(facesContext, component, (String) itemValue);
}
final String formattedValue = ComponentUtils.getFormattedValue(facesContext, component, itemValue);
final boolean contains;
if (submittedValues == null) {
contains = ArrayUtils.contains(values, itemValue);
} else {
contains = ArrayUtils.contains(submittedValues, formattedValue);
}
if (onlySelected != null) {
if (onlySelected) {
if (!contains) {
continue;
}
} else {
if (contains) {
continue;
}
}
}
writer.startElement(HtmlElements.OPTION);
writer.writeAttribute(HtmlAttributes.VALUE, formattedValue, true);
if (item instanceof org.apache.myfaces.tobago.model.SelectItem) {
final String image = ((org.apache.myfaces.tobago.model.SelectItem) item).getImage();
if (image != null) {
final AbstractUIStyle style = (AbstractUIStyle) facesContext.getApplication()
.createComponent(facesContext, Tags.style.componentType(), RendererTypes.Style.name());
style.setTransient(true);
style.setBackgroundImage(image);
style.setSelector(
StyleRenderUtils.encodeIdSelector(component.getClientId(facesContext))
+ " option[value=" + formattedValue + "]");
// XXX This works not in common browsers...
component.getChildren().add(style);
}
}
Markup markup = item instanceof Visual ? ((Visual) item).getMarkup() : Markup.NULL;
if (onlySelected == null && contains) {
writer.writeAttribute(HtmlAttributes.SELECTED, true);
markup = Markup.SELECTED.add(markup);
}
if (item.isDisabled()) {
writer.writeAttribute(HtmlAttributes.DISABLED, true);
markup = Markup.DISABLED.add(markup);
}
writer.writeClassAttribute(optionClass, optionClass.createMarkup(markup));
writer.writeText(item.getLabel());
writer.endElement(HtmlElements.OPTION);
}
}
}
public static void writeDataAttributes(
final FacesContext context, final TobagoResponseWriter writer, final UIComponent component)
throws IOException {
final Map<Object, Object> dataAttributes = ComponentUtils.getDataAttributes(component);
if (dataAttributes == null) {
return;
}
final ELContext elContext = context.getELContext();
for (final Map.Entry<Object, Object> entry : dataAttributes.entrySet()) {
final Object mapKey = entry.getKey();
final String name = mapKey instanceof ValueExpression
? ((ValueExpression) mapKey).getValue(elContext).toString() : mapKey.toString();
final Object mapValue = entry.getValue();
final String value = mapValue instanceof ValueExpression
? ((ValueExpression) mapValue).getValue(elContext).toString() : mapValue.toString();
writer.writeAttribute(DataAttributes.dynamic(name), value, true);
}
}
}