Permalink
Browse files

Added util component for looking up other components by their ID.

  • Loading branch information...
1 parent 32113c7 commit 2286fd4cdd258cc4e6f461d0d7a906a6b2400f3b arjan.tijms committed Jul 4, 2014
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2014 OmniFaces.
+ *
+ * Licensed 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.omnifaces.component.util;
+
+import static java.lang.String.format;
+import static org.omnifaces.component.util.ResolveComponent.PropertyKeys.name;
+import static org.omnifaces.component.util.ResolveComponent.PropertyKeys.scope;
+import static org.omnifaces.util.Components.findComponentRelatively;
+import static org.omnifaces.util.Events.subscribeToViewEvent;
+import static org.omnifaces.util.Faces.isPostback;
+import static org.omnifaces.util.Faces.setRequestAttribute;
+import static org.omnifaces.util.Utils.isEmpty;
+
+import javax.faces.component.FacesComponent;
+import javax.faces.component.UIComponent;
+import javax.faces.event.AbortProcessingException;
+import javax.faces.event.ComponentSystemEvent;
+import javax.faces.event.PostRestoreStateEvent;
+import javax.faces.event.PreRenderViewEvent;
+import javax.faces.event.SystemEvent;
+import javax.faces.event.SystemEventListener;
+import javax.faces.view.facelets.FaceletContext;
+
+import org.omnifaces.el.ReadOnlyValueExpression;
+import org.omnifaces.util.Callback.Returning;
+import org.omnifaces.util.State;
+
+/**
+ * <strong>ResolveComponent</strong> is a utility component via which a component can be looked up by its ID and
+ * a reference to it put in either the "facelet scope" (default) or the request scope.
+ *
+ * @since 2.0
+ * @author Arjan Tijms
+ */
+@FacesComponent(ResolveComponent.COMPONENT_TYPE)
+public class ResolveComponent extends UtilFamily implements FaceletContextConsumer, SystemEventListener {
+
+ public static final String COMPONENT_TYPE = "org.omnifaces.component.util.ResolveComponent";
+
+ private static final String ERROR_COMPONENT_NOT_FOUND =
+ "A component with ID '%s' as specified by the 'for' attribute of the ResolveComponent with Id '%s' could not be found.";
+
+ public static final String DEFAULT_SCOPE = "facelet";
+
+ private UIComponent foundComponent;
+
+ private final State state = new State(getStateHelper());
+
+ enum PropertyKeys {
+ name, scope, /* for */
+ }
+
+ // Actions --------------------------------------------------------------------------------------------------------
+
+ @Override
+ public void setFaceletContext(FaceletContext faceletContext) {
+ if (getScope().equals("facelet")) {
+ faceletContext.getVariableMapper().setVariable(getName(), new ReadOnlyValueExpression(UIComponent.class, new Returning<Object>() { @Override public Object invoke() {
+ return foundComponent;
+ }}));
+ }
+ }
+
+ public ResolveComponent() {
+ if (!isPostback()) { // For an initial (GET) request, there's no restore state event and we use pre-render view
+ subscribeToViewEvent(PreRenderViewEvent.class, this);
+ }
+ }
+
+ @Override
+ public boolean isListenerForSource(Object source) {
+ return true;
+ }
+
+ @Override
+ public void processEvent(SystemEvent event) throws AbortProcessingException {
+ doProcess();
+ }
+
+ @Override
+ public void processEvent(ComponentSystemEvent event) throws AbortProcessingException {
+ if (event instanceof PostRestoreStateEvent) { // For a postback we use the post-restore state event
+ doProcess();
+ }
+ }
+
+ public void doProcess() {
+ String forValue = getFor();
+
+ if (!isEmpty(forValue)) {
+ UIComponent component = findComponentRelatively(this, forValue);
+
+ if (component == null) {
+ component = findComponent(forValue);
+ }
+
+ if (component == null) {
+ throw new IllegalArgumentException(format(ERROR_COMPONENT_NOT_FOUND, forValue, getId()));
+ }
+
+ foundComponent = component;
+
+ if (getScope().equals("request")) {
+ setRequestAttribute(getName(), foundComponent);
+ }
+ }
+ }
+
+
+ // Attribute getters/setters --------------------------------------------------------------------------------------
+
+ public String getName() {
+ return state.get(name);
+ }
+
+ public void setName(String nameValue) {
+ state.put(name, nameValue);
+ }
+
+ public String getFor() {
+ return state.get("for");
+ }
+
+ public void setFor(String nameValue) {
+ state.put("for", nameValue);
+ }
+
+ public String getScope() {
+ return state.get(scope, DEFAULT_SCOPE);
+ }
+
+ public void setScope(String scopeValue) {
+ state.put(scope, scopeValue);
+ }
+}
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 OmniFaces.
+ *
+ * Licensed 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.omnifaces.component.util;
+
+import javax.faces.component.UIComponentBase;
+
+/**
+ * Base class which is to be shared between all components of the Util family.
+ *
+ * @author Arjan Tijms
+ * @since 2.0
+ */
+public abstract class UtilFamily extends UIComponentBase {
+
+ // Public constants -----------------------------------------------------------------------------------------------
+
+ /** The standard component family. */
+ public static final String COMPONENT_FAMILY = "org.omnifaces.component.util";
+
+ // UIComponent overrides ------------------------------------------------------------------------------------------
+
+ /**
+ * Returns {@link #COMPONENT_FAMILY}.
+ */
+ @Override
+ public String getFamily() {
+ return COMPONENT_FAMILY;
+ }
+
+ /**
+ * Returns <code>true</code>.
+ */
+ @Override
+ public boolean getRendersChildren() {
+ return true;
+ }
+
+}
@@ -1961,6 +1961,55 @@ public class ValidateValuesBean implements MultiFieldValidator {
<type>java.lang.String</type>
</attribute>
</tag>
+
+ <tag>
+ <description>
+ <![CDATA[
+ <strong>ResolveComponent</strong> is a utility component via which a component can be looked up by its ID and
+ a reference to it put in either the "facelet scope" (default) or the request scope.
+ ]]>
+ </description>
+ <tag-name>resolveComponent</tag-name>
+ <component>
+ <component-type>org.omnifaces.component.util.ResolveComponent</component-type>
+ <handler-class>org.omnifaces.taghandler.ComponentExtraHandler</handler-class>
+ </component>
+
+ <attribute>
+ <description>
+ <![CDATA[
+ Name under which the component will be made available to EL, scoped to the body of the Facelets tag (default)
+ or to the request.
+ ]]>
+ </description>
+ <name>name</name>
+ <required>true</required>
+ <type>java.lang.String</type>
+ </attribute>
+ <attribute>
+ <description>
+ <![CDATA[
+ ID of the component that will be resolved (looked-up) and if found a reference of it made available to EL.
+ ]]>
+ </description>
+ <name>for</name>
+ <required>true</required>
+ <type>java.lang.String</type>
+ </attribute>
+ <attribute>
+ <description>
+ <![CDATA[
+ Optional scope identifier used to set the scope in which the component reference is inserted. If no scope is specified,
+ the default scope "facelet" will be used.
+ <p>
+ Values values are "facelet" (default) and "request".
+ ]]>
+ </description>
+ <name>scope</name>
+ <required>false</required>
+ <type>java.lang.String</type>
+ </attribute>
+ </tag>
<tag>
<description>

0 comments on commit 2286fd4

Please sign in to comment.