diff --git a/src/main/config/component/focusModifier.xml b/src/main/config/component/focusModifier.xml index 7a17a81..00e7c43 100644 --- a/src/main/config/component/focusModifier.xml +++ b/src/main/config/component/focusModifier.xml @@ -43,5 +43,20 @@ "" + + priority + java.lang.Integer + + If there are more components requesting focus, then component with lowest priority will be focused. + + + + skipped + boolean + + If set to true, component will not be concerned when calculating default focus. + + false + diff --git a/src/main/java/org/richfaces/component/UIFocus.java b/src/main/java/org/richfaces/component/UIFocus.java index ca2922c..5831592 100644 --- a/src/main/java/org/richfaces/component/UIFocus.java +++ b/src/main/java/org/richfaces/component/UIFocus.java @@ -5,32 +5,71 @@ 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; public abstract class UIFocus extends UIComponentBase { +// ------------------------------ FIELDS ------------------------------ + + public static final String COMPONENT_FAMILY = "org.richfaces.Focus"; public static final String COMPONENT_TYPE = "org.richfaces.Focus"; - public static final String COMPONENT_FAMILY = "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"; - public static final String FOCUS_MODIFIER_FACET_NAME = "focusModifier"; +// -------------------------- STATIC METHODS -------------------------- - public Integer getDefaultPriority(FacesContext context) { - UIComponent parentForm = getParent(); + public static UIFocusModifier findModifier(UIComponent component) { + if (component instanceof UIFocusModifier) { + return (UIFocusModifier) component; + } + UIFocusModifier modifier = (UIFocusModifier) component.getFacet(UIFocus.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 UIFocusModifier 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, getTargetComponentId(context)); + return getUIInputChildrenCount(parentForm, component.getId()); } else { - return Integer.MAX_VALUE; + return DEFAULT_PRIORITY; } } + public abstract String getFor(); + + public abstract String getName(); + + public abstract Integer getPriority(); + + public abstract String getSuffix(); + + public abstract String getTargetClientId(); + public String getTargetComponentId(FacesContext context) { String aFor = getFor(); @@ -43,68 +82,49 @@ public abstract class UIFocus extends UIComponentBase { while (clientIdsWithMessages.hasNext()) { allowedClientIds.add(clientIdsWithMessages.next()); } - UIComponent component = getFirstInput(getParentForm(this), allowedClientIds); - return component == null ? null : component.getClientId(context); + final List inputs = new ArrayList(); + 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); } } } - private int getUIInputChildrenCount(UIComponent component, String breakOnId) { - int inputComponentsCount = 0; - for (UIComponent child : component.getChildren()) { - if (child.getId().equals(breakOnId)) { - break; - } - if (child instanceof UIInput) { - inputComponentsCount++; - } else { - int uIInputChildrenCount = getUIInputChildrenCount(child, breakOnId); - inputComponentsCount += uIInputChildrenCount; - } - } - return inputComponentsCount; - } - - public abstract String getFor(); + public abstract String getTiming(); public abstract void setFor(String value); - public abstract String getTargetClientId(); - - public abstract void setTargetClientId(String targetClientId); - - public abstract String getSuffix(); - - public abstract void setSuffix(String value); - - public abstract Integer getPriority(); + public abstract void setName(String name); public abstract void setPriority(Integer value); - public abstract String getTiming(); - - public abstract String getName(); + public abstract void setSuffix(String value); - public abstract void setName(String name); + public abstract void setTargetClientId(String targetClientId); public abstract void setTiming(String timing); - private UIComponent getFirstInput(UIComponent parent, Set allowedClientIds) { - UIComponent input = null; + private void getInputs(UIComponent parent, Set allowedClientIds, List inputs) { FacesContext facesContext = getFacesContext(); for (UIComponent child : parent.getChildren()) { if (child instanceof UIInput && (allowedClientIds.size() == 0 || allowedClientIds.contains(child.getClientId(facesContext)))) { - return child; - } else { - input = getFirstInput(child, allowedClientIds); - if (input != null) { - break; + final UIFocusModifier modifier = findModifier(child); + if (modifier == null || !modifier.isSkipped()) { + inputs.add((UIInput) child); } } + getInputs(child, allowedClientIds, inputs); } - return input; } private UIForm getParentForm(UIComponent component) { @@ -118,4 +138,36 @@ public abstract class UIFocus extends UIComponentBase { return getParentForm(parent); } } + + private int getUIInputChildrenCount(UIComponent component, String breakOnId) { + final Holder childrenCount = new Holder(); + childrenCount.value = 0; + getUIInputChildrenCount(component, breakOnId, childrenCount); + return childrenCount.value; + } + + private boolean getUIInputChildrenCount(UIComponent component, String breakOnId, Holder childrenCount) { + for (UIComponent child : component.getChildren()) { + if (child.getId().equals(breakOnId)) { + return true; + } + if (child instanceof UIInput) { + final UIFocusModifier 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 { +// ------------------------------ FIELDS ------------------------------ + + public T value; + } } diff --git a/src/main/java/org/richfaces/component/UIFocusModifier.java b/src/main/java/org/richfaces/component/UIFocusModifier.java index 387d111..0ce2dfe 100644 --- a/src/main/java/org/richfaces/component/UIFocusModifier.java +++ b/src/main/java/org/richfaces/component/UIFocusModifier.java @@ -11,11 +11,19 @@ public abstract class UIFocusModifier extends UIComponentBase { // -------------------------- OTHER METHODS -------------------------- - public abstract String getTargetClientId(); + public abstract Integer getPriority(); public abstract String getSuffix(); - public abstract void setTargetClientId(String focusClientId); + public abstract String getTargetClientId(); + + public abstract boolean isSkipped(); + + public abstract void setPriority(Integer priority); + + public abstract void setSkipped(boolean skipped); public abstract void setSuffix(String value); + + public abstract void setTargetClientId(String focusClientId); } diff --git a/src/main/java/org/richfaces/renderkit/html/HtmlFocusRenderer.java b/src/main/java/org/richfaces/renderkit/html/HtmlFocusRenderer.java index e76078d..61b7593 100644 --- a/src/main/java/org/richfaces/renderkit/html/HtmlFocusRenderer.java +++ b/src/main/java/org/richfaces/renderkit/html/HtmlFocusRenderer.java @@ -32,6 +32,7 @@ import org.richfaces.component.UIFocusModifier; import javax.faces.FacesException; import javax.faces.component.UIComponent; +import javax.faces.component.UIInput; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import java.io.IOException; @@ -58,6 +59,26 @@ public class HtmlFocusRenderer extends HeaderResourcesRendererBase { return styles; } + private void checkValidity(FacesContext context, UIFocus component) { + String clientId = getUtils().clientId(context, component); + String name = component.getName(); + String timing = component.getTiming(); + String _for = component.getFor(); + String targetClientId = component.getTargetClientId(); + if ((name == null || "".equals(name.trim())) && "onJScall".equals(timing)) { + throw new FacesException( + "The name attribute of the focus component (id='" + clientId + "') must be specified when timing attribute equals to 'onJScall'"); + } + if (name != null && !"".equals(name.trim()) && !"onJScall".equals(timing)) { + throw new FacesException( + "The timing attribute of the focus component (id='" + clientId + "') must be set to 'onJScall' when name attribute is specified"); + } + if ((_for == null || "".equals(_for)) && (targetClientId == null || "".equals(targetClientId)) && !(component.getParent() instanceof UIInput) + && getUtils().getNestingForm(context, component) == null) { + throw new FacesException("Focus component must have either one of 'for' or 'targetClientId' attributes specified or be nested within UIForm or UIInput component"); + } + } + @Override protected void doEncodeEnd(ResponseWriter writer, FacesContext context, UIComponent component) throws IOException { @@ -66,11 +87,8 @@ public class HtmlFocusRenderer extends HeaderResourcesRendererBase { } UIFocus uiFocus = (UIFocus) component; final String clientId = getUtils().clientId(context, component); - checkValidity(clientId, uiFocus.getName(), uiFocus.getTiming()); + checkValidity(context, uiFocus); Integer priority = uiFocus.getPriority(); - if (priority == null) { - priority = uiFocus.getDefaultPriority(context); - } String targetClientId = uiFocus.getTargetClientId(); if (targetClientId == null || "".equals(targetClientId)) { String targetComponentId = uiFocus.getTargetComponentId(context); @@ -83,7 +101,7 @@ public class HtmlFocusRenderer extends HeaderResourcesRendererBase { throw new FacesException("No component with id=" + targetComponentId + " found!"); } targetClientId = forcomp.getClientId(context); - UIFocusModifier modifier = findModifier(forcomp); + UIFocusModifier modifier = UIFocus.findModifier(forcomp); if (modifier != null) { final String modifiedTargetClientId = modifier.getTargetClientId(); if (modifiedTargetClientId != null && !modifiedTargetClientId.equals("")) { @@ -92,6 +110,9 @@ public class HtmlFocusRenderer extends HeaderResourcesRendererBase { suffix = modifier.getSuffix(); } } + if (priority == null) { + priority = uiFocus.calculatePriority(forcomp); + } if (suffix != null && !"".equals(suffix)) { targetClientId += suffix; } @@ -99,6 +120,9 @@ public class HtmlFocusRenderer extends HeaderResourcesRendererBase { if (targetClientId == null || targetClientId.equals("")) { return; } + if (priority == null) { + priority = UIFocus.DEFAULT_PRIORITY; + } writer.startElement(RendererUtils.HTML.SCRIPT_ELEM, null); writer.writeAttribute(RendererUtils.HTML.TYPE_ATTR, "text/javascript", "type"); writer.writeAttribute(RendererUtils.HTML.id_ATTRIBUTE, clientId, RendererUtils.HTML.id_ATTRIBUTE); @@ -119,34 +143,7 @@ public class HtmlFocusRenderer extends HeaderResourcesRendererBase { writer.endElement(RendererUtils.HTML.SCRIPT_ELEM); } - private void checkValidity(String clientId, String name, String timing) { - if ((name == null || "".equals(name.trim())) && "onJScall".equals(timing)) { - throw new FacesException( - "The name attribute of the focus component (id='" + clientId + "') must be specified when timing attribute equals to 'onJScall'"); - } - if (name != null && !"".equals(name.trim()) && !"onJScall".equals(timing)) { - throw new FacesException( - "The timing attribute of the focus component (id='" + clientId + "') must be set to 'onJScall' when name attribute is specified"); - } - } - protected Class getComponentClass() { return UIFocus.class; } - - private UIFocusModifier findModifier(UIComponent component) { - if (component instanceof UIFocusModifier) { - return (UIFocusModifier) component; - } - UIFocusModifier modifier = (UIFocusModifier) component.getFacet(UIFocus.FOCUS_MODIFIER_FACET_NAME); - if (modifier == null) { - for (UIComponent child : component.getChildren()) { - modifier = findModifier(child); - if (modifier != null) { - break; - } - } - } - return modifier; - } }