package org.richfaces.component; import org.richfaces.cdk.annotations.Attribute; import org.richfaces.cdk.annotations.JsfComponent; import org.richfaces.cdk.annotations.JsfRenderer; import org.richfaces.cdk.annotations.Tag; import org.richfaces.cdk.annotations.TagType; import org.richfaces.log.LogFactory; import org.richfaces.log.Logger; import org.richfaces.renderkit.html.HtmlFocusRenderer; import javax.faces.component.UIComponent; import javax.faces.component.UIComponentBase; import javax.faces.component.UIForm; import javax.faces.component.UIInput; import javax.faces.context.FacesContext; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; @JsfComponent(tag = @Tag(name = "focus", type = TagType.Facelets), renderer = @JsfRenderer(family = AbstractFocus.COMPONENT_FAMILY, type = HtmlFocusRenderer.RENDERER_TYPE)) public abstract class AbstractFocus extends UIComponentBase { // ------------------------------ FIELDS ------------------------------ public static final String COMPONENT_FAMILY = "org.richfaces.Focus"; public static final String COMPONENT_TYPE = "org.richfaces.Focus"; public static final int DEFAULT_PRIORITY = Integer.MAX_VALUE; public static final String FOCUS_MODIFIER_FACET_NAME = "focusModifier"; public static final String TIMING_ON_LOAD = "onload"; private static final Logger LOG = LogFactory.getLogger(AbstractFocus.class); // -------------------------- STATIC METHODS -------------------------- public static AbstractFocusModifier findModifier(UIComponent component) { if (component instanceof AbstractFocusModifier) { return (AbstractFocusModifier) component; } AbstractFocusModifier modifier = (AbstractFocusModifier) component.getFacet(AbstractFocus.FOCUS_MODIFIER_FACET_NAME); if (modifier == null) { for (UIComponent child : component.getChildren()) { modifier = findModifier(child); if (modifier != null) { break; } } } return modifier; } // -------------------------- OTHER METHODS -------------------------- public int calculatePriority(UIComponent component) { final AbstractFocusModifier modifier = findModifier(component); if (modifier != null && modifier.getPriority() != null) { return modifier.getPriority(); } UIComponent parentForm = component.getParent(); while (parentForm != null && !(parentForm instanceof UIForm)) { parentForm = parentForm.getParent(); } if (parentForm != null) { return getUIInputChildrenCount(parentForm, component.getId()); } else { return DEFAULT_PRIORITY; } } @Attribute public abstract String getFor(); @Attribute public abstract String getName(); @Attribute public abstract Integer getPriority(); @Attribute public abstract String getSuffix(); @Attribute public abstract String getTargetClientId(); public String getTargetComponentId(FacesContext context) { String aFor = getFor(); if (aFor != null && !"".equals(aFor)) { return aFor; } else { if (!(getParent() instanceof UIInput)) { Set<String> allowedClientIds = new HashSet<String>(); Iterator<String> clientIdsWithMessages = getFacesContext().getClientIdsWithMessages(); while (clientIdsWithMessages.hasNext()) { final String clientId = clientIdsWithMessages.next(); if (clientId != null) { allowedClientIds.add(clientId); } } final List<UIInput> inputs = new ArrayList<UIInput>(); getInputs(getParentForm(this), allowedClientIds, inputs); UIInput inputWithLowestPriority = null; int lowestPriority = Integer.MAX_VALUE; for (UIInput input : inputs) { final int priority = calculatePriority(input); if (priority < lowestPriority) { inputWithLowestPriority = input; lowestPriority = priority; } } return inputWithLowestPriority == null ? null : inputWithLowestPriority.getClientId(context); } else { return getParent().getClientId(context); } } } @Attribute public abstract String getTiming(); private void getInputs(UIComponent parent, Set<String> allowedClientIds, List<UIInput> inputs) { FacesContext facesContext = getFacesContext(); if (isCompositeComponent(parent)) { String parentId = parent.getId(); parent = parent.getFacet(UIComponent.COMPOSITE_FACET_NAME); if (parent == null) { LOG.warn("Composite component " + parentId + " doesn't have facet " + UIComponent.COMPOSITE_FACET_NAME); return; } } for (UIComponent child : parent.getChildren()) { if (child instanceof UIInput && (allowedClientIds.size() == 0 || allowedClientIds.contains(child.getClientId(facesContext)))) { final AbstractFocusModifier modifier = findModifier(child); if (modifier == null || !modifier.isSkipped()) { inputs.add((UIInput) child); } } getInputs(child, allowedClientIds, inputs); } } private UIForm getParentForm(UIComponent component) { UIComponent parent = component.getParent(); if (parent == null) { return null; } if (parent instanceof UIForm) { return (UIForm) parent; } else { return getParentForm(parent); } } private int getUIInputChildrenCount(UIComponent component, String breakOnId) { final Holder<Integer> childrenCount = new Holder<Integer>(); childrenCount.value = 0; getUIInputChildrenCount(component, breakOnId, childrenCount); return childrenCount.value; } private boolean getUIInputChildrenCount(UIComponent component, String breakOnId, Holder<Integer> childrenCount) { for (UIComponent child : component.getChildren()) { if (child.getId().equals(breakOnId)) { return true; } if (child instanceof UIInput) { final AbstractFocusModifier modifier = findModifier(child); if (modifier == null || !modifier.isSkipped()) { childrenCount.value++; } } else { if (getUIInputChildrenCount(child, breakOnId, childrenCount)) { return true; } } } return false; } // -------------------------- INNER CLASSES -------------------------- private class Holder<T> { // ------------------------------ FIELDS ------------------------------ public T value; } }