dev@javaserverfaces.java.net

[REVIEW] JSF_1_1_ROLLING - Optimize Util's passthrough attribute handling

From: Ryan Lubke <Ryan.Lubke_at_Sun.COM>
Date: Thu, 26 Jan 2006 09:59:45 -0800


Optimization of passthrough attribute handling

SECTION: Modified Files
----------------------------
M src/com/sun/faces/util/Util.java
  - Updated hasPassThroughAttributes
    and the two related rendering methods
     * Instead of iterating through the attribute
       arrays, obtain the entrySet() from the
       AttributeMap and check to see if any of those
       attributes qualify as passthrough.
     * When profiling guessNumber hasPassThruAttributes
       and renderPassThruAttributes has cumulative times
       of 26ms and 10ms respectively. With change in place
       time is negligible.


SECTION: Diffs
----------------------------
Index: src/com/sun/faces/util/Util.java
===================================================================
RCS file: /cvs/javaserverfaces-sources/jsf-ri/src/com/sun/faces/util/Util.java,v
retrieving revision 1.143.6.1.2.5
diff -u -r1.143.6.1.2.5 Util.java
--- src/com/sun/faces/util/Util.java 2 Dec 2005 17:17:36 -0000 1.143.6.1.2.5
+++ src/com/sun/faces/util/Util.java 26 Jan 2006 17:53:46 -0000
@@ -11,13 +11,6 @@
 
 package com.sun.faces.util;
 
-import com.sun.faces.RIConstants;
-import com.sun.faces.el.impl.ExpressionEvaluator;
-import com.sun.faces.el.impl.ExpressionEvaluatorImpl;
-import com.sun.faces.renderkit.RenderKitImpl;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
 import javax.faces.FacesException;
 import javax.faces.FactoryFinder;
 import javax.faces.application.Application;
@@ -28,8 +21,8 @@
 import javax.faces.component.UIComponent;
 import javax.faces.component.UISelectItem;
 import javax.faces.component.UISelectItems;
-import javax.faces.context.FacesContext;
 import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
 import javax.faces.context.FacesContextFactory;
 import javax.faces.context.ResponseWriter;
 import javax.faces.convert.Converter;
@@ -43,19 +36,28 @@
 import javax.faces.render.ResponseStateManager;
 import javax.servlet.ServletContext;
 
-import java.io.IOException;
 import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.io.PrintStream;
 import java.lang.reflect.Constructor;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
-import java.util.Locale;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.StringTokenizer;
-import java.util.HashMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.sun.faces.RIConstants;
+import com.sun.faces.el.impl.ExpressionEvaluator;
+import com.sun.faces.el.impl.ExpressionEvaluatorImpl;
+import com.sun.faces.renderkit.RenderKitImpl;
 
 /**
  * <B>Util</B> is a class ...
@@ -329,10 +331,10 @@
      * @see renderBooleanPassthruAttributes
      */
 
- private static String booleanPassthruAttributes[] = {
+ private final static String[] BOOLEAN_PASS_THROUGH_ATTRIBUTES = {
         "disabled",
+ "ismap",
         "readonly",
- "ismap"
     };
 
     /**
@@ -344,7 +346,7 @@
      *
      * @see renderPassthruAttributes
      */
- private static String passthruAttributes[] = {
+ private final static String[] PASS_THROUGH_ATTRIBUTES = {
         "accept",
         "accesskey",
         "alt",
@@ -395,11 +397,18 @@
         "usemap",
         "width"
     };
+ static {
+ Arrays.sort(BOOLEAN_PASS_THROUGH_ATTRIBUTES);
+ Arrays.sort(PASS_THROUGH_ATTRIBUTES);
+ }
 
     //NOTE - "type" was deliberately skipped from the list of passthru
     //attrs above All renderers that need this attribute should manually
     //pass it.
     
+ private static final Iterator EMPTY_ITERATOR =
+ Collections.EMPTY_LIST.iterator();
+
 
 
 //
@@ -445,7 +454,7 @@
      */
 
     public static synchronized String getExceptionMessageString(String messageId,
- Object params[]) {
+ Object params[]) {
         String result = null;
 
         FacesMessage message = MessageFactory.getMessage(messageId, params);
@@ -466,7 +475,7 @@
     }
 
     public static synchronized FacesMessage getExceptionMessage(String messageId,
- Object params[]) {
+ Object params[]) {
         return MessageFactory.getMessage(messageId, params);
     }
 
@@ -578,7 +587,7 @@
                                Boolean.FALSE);
             throw new FacesException(
                 Util.getExceptionMessageString(Util.MISSING_CLASS_ERROR_MESSAGE_ID,
- params),
+ params),
                 e);
         }
         applicationMap.put(RIConstants.HAS_REQUIRED_CLASSES_ATTR, Boolean.TRUE);
@@ -600,59 +609,63 @@
     public static Iterator getSelectItems(FacesContext context,
                                           UIComponent component) {
 
- ArrayList list = new ArrayList();
- Iterator kids = component.getChildren().iterator();
- while (kids.hasNext()) {
- UIComponent kid = (UIComponent) kids.next();
- if (kid instanceof UISelectItem) {
- Object value = ((UISelectItem) kid).getValue();
- if (value == null) {
- UISelectItem item = (UISelectItem) kid;
- list.add(new SelectItem(item.getItemValue(),
- item.getItemLabel(),
- item.getItemDescription(),
- item.isItemDisabled()));
- } else if (value instanceof SelectItem) {
- list.add(value);
- } else {
- throw new IllegalArgumentException(Util.getExceptionMessageString(
- Util.CONVERSION_ERROR_MESSAGE_ID));
- }
- } else if (kid instanceof UISelectItems && null != context) {
- Object value = ((UISelectItems) kid).getValue();
- if (value instanceof SelectItem) {
- list.add(value);
- } else if (value instanceof SelectItem[]) {
- SelectItem items[] = (SelectItem[]) value;
- for (int i = 0; i < items.length; i++) {
- list.add(items[i]);
- }
- } else if (value instanceof Collection) {
- Iterator elements = ((Collection) value).iterator();
- while (elements.hasNext()) {
- list.add(elements.next());
+ if (component.getChildCount() > 0) {
+ ArrayList list = new ArrayList();
+ Iterator kids = component.getChildren().iterator();
+ while (kids.hasNext()) {
+ UIComponent kid = (UIComponent) kids.next();
+ if (kid instanceof UISelectItem) {
+ Object value = ((UISelectItem) kid).getValue();
+ if (value == null) {
+ UISelectItem item = (UISelectItem) kid;
+ list.add(new SelectItem(item.getItemValue(),
+ item.getItemLabel(),
+ item.getItemDescription(),
+ item.isItemDisabled()));
+ } else if (value instanceof SelectItem) {
+ list.add(value);
+ } else {
+ throw new IllegalArgumentException(Util.getExceptionMessageString(
+ Util.CONVERSION_ERROR_MESSAGE_ID));
                     }
- } else if (value instanceof Map) {
- Iterator keys = ((Map) value).keySet().iterator();
- while (keys.hasNext()) {
- Object key = keys.next();
- if (key == null) {
- continue;
+ } else if (kid instanceof UISelectItems && null != context) {
+ Object value = ((UISelectItems) kid).getValue();
+ if (value instanceof SelectItem) {
+ list.add(value);
+ } else if (value instanceof SelectItem[]) {
+ SelectItem items[] = (SelectItem[]) value;
+ for (int i = 0; i < items.length; i++) {
+ list.add(items[i]);
+ }
+ } else if (value instanceof Collection) {
+ Iterator elements = ((Collection) value).iterator();
+ while (elements.hasNext()) {
+ list.add(elements.next());
                         }
- Object val = ((Map) value).get(key);
- if (val == null) {
- continue;
+ } else if (value instanceof Map) {
+ Iterator keys = ((Map) value).keySet().iterator();
+ while (keys.hasNext()) {
+ Object key = keys.next();
+ if (key == null) {
+ continue;
+ }
+ Object val = ((Map) value).get(key);
+ if (val == null) {
+ continue;
+ }
+ list.add(new SelectItem(val.toString(),
+ key.toString(),
+ null));
                         }
- list.add(new SelectItem(val.toString(), key.toString(),
- null));
+ } else {
+ throw new IllegalArgumentException(Util.getExceptionMessageString(
+ Util.CONVERSION_ERROR_MESSAGE_ID));
                     }
- } else {
- throw new IllegalArgumentException(Util.getExceptionMessageString(
- Util.CONVERSION_ERROR_MESSAGE_ID));
                 }
             }
+ return (list.iterator());
         }
- return (list.iterator());
+ return EMPTY_ITERATOR;
 
     }
 
@@ -718,45 +731,31 @@
 
     /**
      * @return true if the component has any passthru attributes
- */
- // PENDING() it would be much more performant to have the tag be
- // aware of the passthru attributes and have it set a
- // "hasPassthruAttributes" attribute to true if the component has
- // any.
-
+ */
     public static boolean hasPassThruAttributes(UIComponent component) {
         if (null == component) {
             return false;
         }
-
- boolean result = false;
+
         Map attrs = component.getAttributes();
- if (null == attrs) {
+ if (attrs.size() == 0) {
             return false;
         }
- int i = 0;
- Object attrVal;
- String empty = "";
- for (i = 0; i < passthruAttributes.length; i++) {
- if (null != (attrVal = attrs.get(passthruAttributes[i]))
- &&
- !empty.equals(attrVal)) {
- result = true;
- break;
- }
- }
- if (!result) {
- for (i = 0; i < booleanPassthruAttributes.length; i++) {
- if (null !=
- (attrVal = attrs.get(booleanPassthruAttributes[i]))
- &&
- !empty.equals(attrVal)) {
- result = true;
- break;
- }
+
+ // compare attribute map keys
+ String[] keys =
+ (String[]) attrs.keySet().toArray(new String[attrs.size()]);
+
+
+ for (int i = 0; i < keys.length; i++) {
+ if (Arrays.binarySearch(PASS_THROUGH_ATTRIBUTES, keys[i]) > -1
+ || Arrays.binarySearch(BOOLEAN_PASS_THROUGH_ATTRIBUTES, keys[i]) > -1)
+ {
+ return true;
             }
         }
- return result;
+
+ return false;
     }
 
 
@@ -771,53 +770,37 @@
      * Render any boolean "passthru" attributes.
      * <P>
      *
- * @see passthruAttributes
+ * @see #BOOLEAN_PASS_THROUGH_ATTRIBUTES
      */
     public static void renderBooleanPassThruAttributes(ResponseWriter writer,
                                                        UIComponent component,
                                                        String[] excludes)
         throws IOException {
         Util.doAssert(null != writer);
- Util.doAssert(null != component);
+ Util.doAssert(null != component);
+
+ Map attrs = component.getAttributes();
+
+ if (attrs.size() == 0) {
+ return;
+ }
+
+ if (excludes != null && excludes.length != 0) {
+ Arrays.sort(excludes);
+ }
 
- int i = 0, len = booleanPassthruAttributes.length, j,
- jLen = (null != excludes ? excludes.length : 0);
- Object value = null;
- boolean result;
- boolean skip = false;
- for (i = 0; i < len; i++) {
- skip = false;
- if (null != excludes) {
- for (j = 0; j < jLen; j++) {
- if (null != excludes[j] &&
- excludes[j].equals(booleanPassthruAttributes[i])) {
- skip = true;
- break;
- }
- }
- }
- if (skip) {
+ for (Iterator i = attrs.entrySet().iterator(); i.hasNext();) {
+ Map.Entry entry = (Map.Entry) i.next();
+ String key = (String) entry.getKey();
+
+ if (excludes != null && Arrays.binarySearch(excludes, key) > -1) {
                 continue;
             }
 
- value =
- component.getAttributes().get(booleanPassthruAttributes[i]);
- if (value != null) {
- if (value instanceof Boolean) {
- result = ((Boolean) value).booleanValue();
- } else {
- if (!(value instanceof String)) {
- value = value.toString();
- }
- result = (new Boolean((String) value)).booleanValue();
- }
- //PENDING(rogerk) will revisit "null" param soon..
- if (result) {
- // NOTE: render things like readonly="readonly" here
- writer.writeAttribute(booleanPassthruAttributes[i],
- booleanPassthruAttributes[i],
- booleanPassthruAttributes[i]);
- // NOTE: otherwise render nothing
+ if (Arrays.binarySearch(BOOLEAN_PASS_THROUGH_ATTRIBUTES, key) > -1) {
+ if (Boolean.valueOf(entry.getValue().toString()).booleanValue())
+ {
+ writer.writeAttribute(key, key, key);
                 }
             }
         }
@@ -837,7 +820,7 @@
      * set of HTML4 attributes that fall into this bucket. Examples are
      * all the javascript attributes, alt, rows, cols, etc. <P>
      *
- * @see passthruAttributes
+ * @see #PASS_THROUGH_ATTRIBUTES
      */
     public static void renderPassThruAttributes(ResponseWriter writer,
                                                 UIComponent component,
@@ -845,34 +828,30 @@
         throws IOException {
         Util.doAssert(null != writer);
         Util.doAssert(null != component);
+
+ Map attrs = component.getAttributes();
+
+ if (attrs.size() == 0) {
+ return;
+ }
+
+ if (excludes != null && excludes.length != 0) {
+ Arrays.sort(excludes);
+ }
 
- int i = 0, len = passthruAttributes.length, j,
- jLen = (null != excludes ? excludes.length : 0);
- Object value = null;
- boolean skip = false;
- for (i = 0; i < len; i++) {
- skip = false;
- if (null != excludes) {
- for (j = 0; j < jLen; j++) {
- if (null != excludes[j] &&
- excludes[j].equals(passthruAttributes[i])) {
- skip = true;
- break;
- }
- }
- }
- if (skip) {
+ for (Iterator i = attrs.entrySet().iterator(); i.hasNext();) {
+ Map.Entry entry = (Map.Entry) i.next();
+ String key = (String) entry.getKey();
+
+ if (excludes != null && Arrays.binarySearch(excludes, key) > -1) {
                 continue;
             }
 
- value = component.getAttributes().get(passthruAttributes[i]);
- if (value != null && shouldRenderAttribute(value)) {
- if (!(value instanceof String)) {
- value = value.toString();
+ if (Arrays.binarySearch(BOOLEAN_PASS_THROUGH_ATTRIBUTES, key) > -1) {
+ Object value = entry.getValue();
+ if (value != null && shouldRenderAttribute(value)) {
+ writer.writeAttribute(key, value.toString(), key);
                 }
- //PENDING(rogerk) will revisit "null" param soon..
- writer.writeAttribute(passthruAttributes[i], value,
- passthruAttributes[i]);
             }
         }
     }
@@ -891,25 +870,25 @@
             Boolean.FALSE.booleanValue()) {
             return false;
         } else if (attributeVal instanceof Integer &&
- ((Integer) attributeVal).intValue() == Integer.MIN_VALUE) {
+ ((Integer) attributeVal).intValue() == Integer.MIN_VALUE) {
             return false;
         } else if (attributeVal instanceof Double &&
- ((Double) attributeVal).doubleValue() == Double.MIN_VALUE) {
+ ((Double) attributeVal).doubleValue() == Double.MIN_VALUE) {
             return false;
         } else if (attributeVal instanceof Character &&
- ((Character) attributeVal).charValue() == Character.MIN_VALUE) {
+ ((Character) attributeVal).charValue() == Character.MIN_VALUE) {
             return false;
         } else if (attributeVal instanceof Float &&
- ((Float) attributeVal).floatValue() == Float.MIN_VALUE) {
+ ((Float) attributeVal).floatValue() == Float.MIN_VALUE) {
             return false;
         } else if (attributeVal instanceof Short &&
- ((Short) attributeVal).shortValue() == Short.MIN_VALUE) {
+ ((Short) attributeVal).shortValue() == Short.MIN_VALUE) {
             return false;
         } else if (attributeVal instanceof Byte &&
- ((Byte) attributeVal).byteValue() == Byte.MIN_VALUE) {
+ ((Byte) attributeVal).byteValue() == Byte.MIN_VALUE) {
             return false;
         } else if (attributeVal instanceof Long &&
- ((Long) attributeVal).longValue() == Long.MIN_VALUE) {
+ ((Long) attributeVal).longValue() == Long.MIN_VALUE) {
             return false;
         }
         return true;
@@ -1092,10 +1071,10 @@
             Map requestMap = context.getExternalContext().getRequestMap();
             RenderKitFactory factory = (RenderKitFactory)
                   requestMap.get(RENDER_KIT_IMPL_REQ);
- if (factory != null) {
+ if (factory != null) {
                 renderKit = factory.getRenderKit(context, renderKitId);
- } else {
- factory = (RenderKitFactory)
+ } else {
+ factory = (RenderKitFactory)
                       FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
                 if (factory == null) {
                     throw new IllegalStateException();
@@ -1105,7 +1084,7 @@
                 renderKit = factory.getRenderKit(context, renderKitId);
             }
         }
-
+
         return renderKit.getResponseStateManager();
     }
 
@@ -1303,7 +1282,7 @@
         }
         return expression;
     }
-
+
     //
     // General Methods
     //
@@ -1352,12 +1331,12 @@
      * @return the scope of the expression
      */
     public static String getScope(String valueBinding,
- String [] outString) throws ReferenceSyntaxException {
+ String [] outString) throws ReferenceSyntaxException {
         if (valueBinding == null || 0 == valueBinding.length()) {
             return null;
         }
- valueBinding = stripBracketsIfNecessary(valueBinding);
-
+ valueBinding = stripBracketsIfNecessary(valueBinding);
+
         int segmentIndex = getFirstSegmentIndex(valueBinding);
 
         //examine first segment and see if it is a scope
@@ -1365,17 +1344,17 @@
 
         if (segmentIndex > 0) {
             //get first segment designated by a "." or "["
- identifier = valueBinding.substring(0, segmentIndex);
+ identifier = valueBinding.substring(0, segmentIndex);
         }
 
         //check to see if the identifier is a named scope.
 
         FacesContext context = FacesContext.getCurrentInstance();
         ExternalContext ec = context.getExternalContext();
-
- if (null != outString) {
- outString[0] = identifier;
- }
+
+ if (null != outString) {
+ outString[0] = identifier;
+ }
         if (identifier.equalsIgnoreCase(RIConstants.REQUEST_SCOPE)) {
             return RIConstants.REQUEST;
         }
@@ -1386,30 +1365,30 @@
             return RIConstants.APPLICATION;
         }
 
- // handle implicit objects
+ // handle implicit objects
         if (identifier.equalsIgnoreCase(RIConstants.INIT_PARAM_IMPLICIT_OBJ)) {
- return RIConstants.APPLICATION;
- }
+ return RIConstants.APPLICATION;
+ }
         if (identifier.equalsIgnoreCase(RIConstants.COOKIE_IMPLICIT_OBJ)) {
- return RIConstants.REQUEST;
- }
+ return RIConstants.REQUEST;
+ }
         if (identifier.equalsIgnoreCase(RIConstants.FACES_CONTEXT_IMPLICIT_OBJ)) {
- return RIConstants.REQUEST;
- }
+ return RIConstants.REQUEST;
+ }
         if (identifier.equalsIgnoreCase(RIConstants.HEADER_IMPLICIT_OBJ)) {
- return RIConstants.REQUEST;
- }
+ return RIConstants.REQUEST;
+ }
         if (identifier.equalsIgnoreCase(RIConstants.HEADER_VALUES_IMPLICIT_OBJ)) {
- return RIConstants.REQUEST;
+ return RIConstants.REQUEST;
         }
         if (identifier.equalsIgnoreCase(RIConstants.PARAM_IMPLICIT_OBJ)) {
- return RIConstants.REQUEST;
+ return RIConstants.REQUEST;
         }
         if (identifier.equalsIgnoreCase(RIConstants.PARAM_VALUES_IMPLICIT_OBJ)) {
- return RIConstants.REQUEST;
+ return RIConstants.REQUEST;
         }
         if (identifier.equalsIgnoreCase(RIConstants.VIEW_IMPLICIT_OBJ)) {
- return RIConstants.REQUEST;
+ return RIConstants.REQUEST;
         }
 
         //No scope was provided in the expression so check for the
@@ -1438,20 +1417,20 @@
      */
 
     public static List getExpressionsFromString(String expressionString) throws ReferenceSyntaxException {
- if (null == expressionString) {
- return Collections.EMPTY_LIST;
- }
- List result = new ArrayList();
- int i, j, len = expressionString.length(), cur = 0;
- while (cur < len &&
- -1 != (i = expressionString.indexOf("#{", cur))) {
- if (-1 == (j = expressionString.indexOf("}", i + 2))) {
- throw new ReferenceSyntaxException(Util.getExceptionMessageString(Util.INVALID_EXPRESSION_ID, new Object[]{expressionString}));
- }
- cur = j + 1;
- result.add(expressionString.substring(i, cur));
- }
- return result;
+ if (null == expressionString) {
+ return Collections.EMPTY_LIST;
+ }
+ List result = new ArrayList();
+ int i, j, len = expressionString.length(), cur = 0;
+ while (cur < len &&
+ -1 != (i = expressionString.indexOf("#{", cur))) {
+ if (-1 == (j = expressionString.indexOf("}", i + 2))) {
+ throw new ReferenceSyntaxException(Util.getExceptionMessageString(Util.INVALID_EXPRESSION_ID, new Object[]{expressionString}));
+ }
+ cur = j + 1;
+ result.add(expressionString.substring(i, cur));
+ }
+ return result;
     }
 
     /**
@@ -1488,17 +1467,17 @@
      * @return the String representation ofthe stack trace obtained by
      * calling getStackTrace() on the passed in exception. If null is
      * passed in, we return the empty String.
- */
+ */
 
     public static String getStackTraceString(Throwable e) {
- if (null == e) {
- return "";
- }
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- PrintStream ps = new PrintStream(baos);
- e.printStackTrace(ps);
- return baos.toString();
+ if (null == e) {
+ return "";
+ }
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos);
+ e.printStackTrace(ps);
+ return baos.toString();
     }
 
 } // end of class Util