[jsr-314-open-mirror] [jsr-314-open] Proposal: small addition to composite component metadata specification

Ed Burns edward.burns at oracle.com
Mon Aug 23 21:44:12 EDT 2010


https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=1782

The proposed solution is to enhance the composite component metadata
specification to include information about the list of composite
component attributes for which default attribute values have been
declared.

This information is used in the Facelets tag layer in the code that
installs the ValueExpression for any child attributes in the using page.
In this case, the MetadataHandler manually removes the attribute from
the attributes map if there is a ValueExpression for an attribute for
which there also is a composite component attribute.

SECTION: Modified Files
----------------------------
M Spec section 3.6.2.1 Composite Component Metadata

Add a new paragraph at the end of the section, which would fall under
the bullet that describes the runtime representation of the
<cc:attribute> element.  

  The composite component BeanDescriptior must return a
  Collection<String> when its getValue() method is called with an
  argument equal to the value of the symbolic constant
  UIComponent.ATTRS_WITH_DECLARED_DEFAULT_VALUES.  The
  Collection<String> must contain the names of any <cc:attribute>
  elements for which the default attribute was specified, or null, if
  none of the attributes have been given a default value.

M       jsf-api/src/main/java/javax/faces/component/UIComponent.java

- Add constant:

+     * <p class="changed_added_2_1">This constant enables one to quickly discover
+     * the names of the declared composite component attributes that have been
+     * given default values by the composite component author.  The information
+     * is exposed as a <code>Collection&lt;String&gt;</code> returned from the
+     * <code>getValue()</code> method on the <em>composite component
+     * BeanDescriptor</em>, when this constant is passed as the argument.</p>
+     *
+     * @since 2.1
+     */
+    public static final String ATTRS_WITH_DECLARED_DEFAULT_VALUES =
+            "javax.faces.component.ATTR_NAMES_WITH_DEFAULT_VALUES";

M       jsf-ri/src/main/java/com/sun/faces/facelets/tag/jsf/CompositeComponentTagHandler.java

- Consider the inner class CompositeExpressionMetadata.  This class
  deals with attributes of a composite component instance in a using
  page that are of type ValueExpressionsp For example:

		<tmo:block collapsable="#{'true'}"/>

  If the corresponding attribute has been declared with a default value
  in the composite component declaration, such as:

	<composite:interface>
		<composite:attribute name="collapsable" required="false" 
default="false"/>
	</composite:interface>

  then the default attribute will have already been stored in the
  attributes map of the composite component by the time the
  CompositeExpressionMetadata.applyMetadata() method is called.  (See
  ApplicationImpl.pushDeclaredDefaultValuesToAttributesMap()).  This
  change uses the new element of composite component metadata to inspect
  if the attribute in question has a default value.  If so, we remove
  the attribute from the component attributes map so that the one from
  the using page is used, instead of the default value.

M       jsf-ri/src/main/java/com/sun/faces/application/ApplicationImpl.java

- in createComponent(String,Resource), and
  pushDeclaredDefaultValuesToAttributesMap(), implement the new portion
  of the composite component metadata specification.

M       jsf-ri/systest/src/com/sun/faces/composite/CompositeComponentsTestCase.java

- new testcase

SECTION: Diffs
----------------------------
Index: jsf-api/src/main/java/javax/faces/component/UIComponent.java
===================================================================
--- jsf-api/src/main/java/javax/faces/component/UIComponent.java	(revision 8566)
+++ jsf-api/src/main/java/javax/faces/component/UIComponent.java	(working copy)
@@ -210,6 +210,19 @@
      */
     public static final String COMPOSITE_FACET_NAME = "javax.faces.component.COMPOSITE_FACET_NAME";
 
+    /**
+     * <p class="changed_added_2_1">This constant enables one to quickly discover
+     * the names of the declared composite component attributes that have been
+     * given default values by the composite component author.  The information
+     * is exposed as a <code>Collection&lt;String&gt;</code> returned from the
+     * <code>getValue()</code> method on the <em>composite component
+     * BeanDescriptor</em>, when this constant is passed as the argument.</p>
+     *
+     * @since 2.1
+     */
+    public static final String ATTRS_WITH_DECLARED_DEFAULT_VALUES =
+            "javax.faces.component.ATTR_NAMES_WITH_DEFAULT_VALUES";
+
     enum PropertyKeysPrivate {
         attributesThatAreSet
     }
Index: jsf-ri/src/main/java/com/sun/faces/facelets/tag/jsf/CompositeComponentTagHandler.java
===================================================================
--- jsf-ri/src/main/java/com/sun/faces/facelets/tag/jsf/CompositeComponentTagHandler.java	(revision 8566)
+++ jsf-ri/src/main/java/com/sun/faces/facelets/tag/jsf/CompositeComponentTagHandler.java	(working copy)
@@ -65,6 +65,7 @@
 import com.sun.faces.util.RequestStateManager;
 import com.sun.faces.util.Util;
 import com.sun.faces.util.FacesLogger;
+import java.beans.BeanDescriptor;
 
 import javax.el.ELException;
 import javax.el.ValueExpression;
@@ -95,6 +96,7 @@
 import java.beans.IntrospectionException;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
@@ -599,6 +601,18 @@
                 ValueExpression ve = attr.getValueExpression(ctx, type);
                 UIComponent cc = (UIComponent) instance;
                 assert (UIComponent.isCompositeComponent(cc));
+                Map<String, Object> attrs = cc.getAttributes();
+                BeanInfo componentMetadata = (BeanInfo) attrs.get(UIComponent.BEANINFO_KEY);
+                BeanDescriptor desc = componentMetadata.getBeanDescriptor();
+                Collection<String> attributesWithDeclaredDefaultValues = (Collection<String>)
+                        desc.getValue(UIComponent.ATTRS_WITH_DECLARED_DEFAULT_VALUES);
+                if (null != attributesWithDeclaredDefaultValues &&
+                        attributesWithDeclaredDefaultValues.contains(name)) {
+                    // It is necessary to remove the value from the attribute
+                    // map because the ELexpression transparancy doesn't know
+                    // about the value's existence.
+                    attrs.remove(name);
+                }
                 cc.setValueExpression(name, ve);
 
             }
Index: jsf-ri/src/main/java/com/sun/faces/application/ApplicationImpl.java
===================================================================
--- jsf-ri/src/main/java/com/sun/faces/application/ApplicationImpl.java	(revision 8566)
+++ jsf-ri/src/main/java/com/sun/faces/application/ApplicationImpl.java	(working copy)
@@ -1031,6 +1031,8 @@
         PropertyDescriptor[] declaredAttributes = componentMetadata.getPropertyDescriptors();
         Object defaultValue;
         String key;
+        Collection<String> attributesWithDeclaredDefaultValues = null;
+
         for (PropertyDescriptor cur : declaredAttributes) {
             defaultValue = cur.getValue("default");
             if (null != defaultValue) {
@@ -1043,6 +1045,19 @@
                 // ensure this attribute is not a method-signature.  method-signature
                 // declared default values are handled in retargetMethodExpressions.
                 if (null == cur.getValue("method-signature") || null != cur.getValue("type")) {
+
+                    if (null == attributesWithDeclaredDefaultValues) {
+                        BeanDescriptor desc = componentMetadata.getBeanDescriptor();
+                        attributesWithDeclaredDefaultValues = (Collection<String>)
+                                desc.getValue(UIComponent.ATTRS_WITH_DECLARED_DEFAULT_VALUES);
+                        if (null == attributesWithDeclaredDefaultValues) {
+                            attributesWithDeclaredDefaultValues = new ArrayList<String>();
+                            desc.setValue(UIComponent.ATTRS_WITH_DECLARED_DEFAULT_VALUES,
+                                    attributesWithDeclaredDefaultValues);
+                        }
+                    }
+                    attributesWithDeclaredDefaultValues.add(key);
+
                     attrs.put(key, defaultValue);
                 }
             }
Index: jsf-ri/systest/src/com/sun/faces/composite/CompositeComponentsTestCase.java
===================================================================
--- jsf-ri/systest/src/com/sun/faces/composite/CompositeComponentsTestCase.java	(revision 8566)
+++ jsf-ri/systest/src/com/sun/faces/composite/CompositeComponentsTestCase.java	(working copy)
@@ -963,6 +963,13 @@
         assertTrue(text.contains("The following facets(s) are required, but no facets have been supplied for them: table."));
     }
 
+    public void testDefaultAttributeValueELOverrides() throws Exception {
+        HtmlPage page = getPage("/faces/composite/issue-1782-using.xhtml");
+        String text = page.asText();
+        System.out.println(text);
+        assertTrue(text.matches("(?s).*collapsable\\s=\\strue.*"));
+    }
+
     // --------------------------------------------------------- Private Methods
 
 



-- 
| edward.burns at oracle.com | office: +1 407 458 0017
| homepage:               | http://ridingthecrest.com/
| 14 work days until JSF 2.1 Milestone 3


More information about the jsr-314-open-mirror mailing list