You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lo...@apache.org on 2010/06/14 17:44:56 UTC

svn commit: r954522 - in /myfaces/tobago/trunk: tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/ tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/ tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/ tobago-co...

Author: lofwyr
Date: Mon Jun 14 15:44:55 2010
New Revision: 954522

URL: http://svn.apache.org/viewvc?rev=954522&view=rev
Log:
TOBAGO-893: Optimize StyleClasses
 - new class "Classes" which builds the style classes like a factory (caching possible)

Added:
    myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/Aspect.java
      - copied, changed from r953303, myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/StyleClasses.java
    myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/Classes.java
      - copied, changed from r953303, myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/StyleClasses.java
    myfaces/tobago/trunk/tobago-core/src/test/java/org/apache/myfaces/tobago/renderkit/css/
    myfaces/tobago/trunk/tobago-core/src/test/java/org/apache/myfaces/tobago/renderkit/css/ClassesUnitTest.java
Modified:
    myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/StyleClasses.java
    myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/TobagoResponseWriter.java
    myfaces/tobago/trunk/tobago-core/src/test/java/org/apache/myfaces/tobago/renderkit/html/StyleClassesUnitTest.java
    myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/java/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/tag/TreeNodeRenderer.java

Copied: myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/Aspect.java (from r953303, myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/StyleClasses.java)
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/Aspect.java?p2=myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/Aspect.java&p1=myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/StyleClasses.java&r1=953303&r2=954522&rev=954522&view=diff
==============================================================================
--- myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/StyleClasses.java (original)
+++ myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/Aspect.java Mon Jun 14 15:44:55 2010
@@ -1,4 +1,4 @@
-package org.apache.myfaces.tobago.renderkit.html;
+package org.apache.myfaces.tobago.renderkit.css;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
@@ -17,268 +17,24 @@ package org.apache.myfaces.tobago.render
  * limitations under the License.
  */
 
-import org.apache.commons.collections.set.ListOrderedSet;
-import org.apache.myfaces.tobago.component.Attributes;
-import org.apache.myfaces.tobago.component.SupportsMarkup;
-import org.apache.myfaces.tobago.context.Markup;
-import org.apache.myfaces.tobago.context.Theme;
-import org.apache.myfaces.tobago.internal.util.Deprecation;
-import org.apache.myfaces.tobago.util.ComponentUtils;
-import org.apache.myfaces.tobago.util.VariableResolverUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.faces.component.UIComponent;
-import javax.faces.component.UIInput;
-import javax.faces.context.FacesContext;
-import java.io.Serializable;
-import java.util.Iterator;
 import java.util.Locale;
-import java.util.Map;
-
-public class StyleClasses implements Serializable {
-
-  private static final long serialVersionUID = 3738052927067803517L;
-
-  private static final Logger LOG = LoggerFactory.getLogger(StyleClasses.class);
-
-  public static final char SEPARATOR = '-';
-  public static final String PREFIX = "tobago" + SEPARATOR;
-  public static final String MARKUP = SEPARATOR + "markup" + SEPARATOR;
-
-  private ListOrderedSet classes;
-
-  public StyleClasses() {
-    classes = new ListOrderedSet();
-  }
-
-  /**
-   * Creates a StyleClasses element and adds one entry for a sub-component with the name of the renderer.
-   * E. g.: UITreeNode + "icon" -> tobago-treeNode-icon
-   */
-  public StyleClasses(UIComponent component, String sub) {
-    this();
-    addClass(toRendererName(component.getRendererType()), sub);
-  }
-
-  private StyleClasses(StyleClasses base) {
-    this();
-    classes.addAll(base.classes);
-  }
-
-  public static StyleClasses ensureStyleClasses(UIComponent component) {
-    Map attributes = component.getAttributes();
-    StyleClasses classes = (StyleClasses) attributes.get(Attributes.STYLE_CLASS);
-    if (classes == null) {
-      classes = new StyleClasses();
-      attributes.put(Attributes.STYLE_CLASS, classes);
-    }
-    return classes;
-  }
-
-  public static StyleClasses ensureStyleClassesCopy(UIComponent component) {
-    return new StyleClasses(ensureStyleClasses(component));
-  }
-
-  @Deprecated
-  public void addFullQualifiedClass(String clazz) {
-    classes.add(clazz);
-  }
-
-  @Deprecated
-  public void removeFullQualifiedClass(String clazz) {
-    classes.remove(clazz);
-  }
-
-  public void addClass(String renderer, String sub) {
-    classes.add(nameOfClass(renderer, sub));
-  }
-
-  public void removeClass(String renderer, String sub) {
-    classes.remove(nameOfClass(renderer, sub));
-  }
-
-  public boolean isEmpty() {
-    return classes.isEmpty();
-  }
-
-  private String nameOfClass(String renderer, String sub) {
-    StringBuilder builder = new StringBuilder(PREFIX);
-    builder.append(renderer);
-    builder.append(SEPARATOR);
-    builder.append(sub);
-    return builder.toString();
-  }
-
-  public void addMarkupClass(String renderer, String markup) {
-    addMarkupClass(renderer, null, markup);
-  }
-
-  public void removeMarkupClass(String renderer, String markup) {
-    removeMarkupClass(renderer, null, markup);
-  }
-
-  public void addMarkupClass(String renderer, String sub, String markup) {
-    classes.add(nameOfMarkupClass(renderer, sub, markup));
-  }
 
-  public void removeMarkupClass(String renderer, String sub, String markup) {
-    classes.remove(nameOfMarkupClass(renderer, sub, markup));
-  }
-
-  private String nameOfMarkupClass(String renderer, String sub, String markup) {
-    StringBuilder builder = new StringBuilder(PREFIX);
-    builder.append(renderer);
-    if (sub != null) {
-      builder.append(SEPARATOR);
-      builder.append(sub);
-    }
-    builder.append(MARKUP);
-    builder.append(markup);
-    return builder.toString();
-  }
-
-  public void addMarkupClass(UIComponent component, String rendererName) {
-    if (component instanceof SupportsMarkup) {
-      addMarkupClass((SupportsMarkup) component, rendererName, null);
-    }
-  }
-
-  public void addMarkupClass(SupportsMarkup supportsMarkup, String rendererName, String sub) {
-    Markup m = supportsMarkup.getMarkup();
-    if (m != null) {
-      for (String markup : m) {
-        Theme theme = VariableResolverUtils.resolveClientProperties(FacesContext.getCurrentInstance()).getTheme();
-        if (theme.getRenderersConfig().isMarkupSupported(rendererName, markup)) {
-          addMarkupClass(rendererName, sub, markup);
-        } else if ("none".equals(markup)) {
-          Deprecation.LOG.warn("Markup 'none' is deprecated, please use a NULL pointer instead.");
-        } else {
-          LOG.warn("Unknown markup='" + markup + "'");
-        }
-      }
-    }
-  }
-
-  public void addAspectClass(String renderer, Aspect aspect) {
-    classes.add(nameOfAspectClass(renderer, aspect));
-  }
-
-  public void removeAspectClass(String renderer, Aspect aspect) {
-    classes.remove(nameOfAspectClass(renderer, aspect));
-  }
-
-  private String nameOfAspectClass(String renderer, Aspect aspect) {
-    StringBuilder builder = new StringBuilder(PREFIX);
-    builder.append(renderer);
-    builder.append(aspect);
-    return builder.toString();
-  }
-
-  public void addAspectClass(String renderer, String sub, Aspect aspect) {
-    classes.add(nameOfAspectClass(renderer, sub, aspect));
-  }
-
-  public void removeAspectClass(String renderer, String sub, Aspect aspect) {
-    classes.remove(nameOfAspectClass(renderer, sub, aspect));
-  }
+public enum Aspect {
 
-  private String nameOfAspectClass(String renderer, String sub, Aspect aspect) {
-    StringBuilder builder = new StringBuilder(PREFIX);
-    builder.append(renderer);
-    builder.append(SEPARATOR);
-    builder.append(sub);
-    builder.append(aspect);
-    return builder.toString();
-  }
-
-  public void addClasses(StyleClasses styleClasses) {
-    for (String clazz : (Iterable<String>) styleClasses.classes) {
-      classes.add(clazz);
-    }
-  }
-
-  public void removeClass(String clazz) {
-    classes.remove(clazz);
-  }
-
-  public void removeTobagoClasses(String rendererName) {
-    for (Iterator i = classes.iterator(); i.hasNext();) {
-      String clazz = (String) i.next();
-      if (clazz.startsWith(PREFIX + rendererName)) {
-        i.remove();
-      }
-    }
-  }
-
-  public void updateClassAttributeAndMarkup(UIComponent component, String rendererName) {
-    updateClassAttribute(component, rendererName);
-    addMarkupClass(component, rendererName);
-  }
+  DEFAULT,
+  DISABLED,
+  READONLY,
+  INLINE,
+  ERROR,
+  REQUIRED;
 
-  public void updateClassAttribute(UIComponent component, String rendererName) {
-    // first remove old tobago-<rendererName>-<type> classes from class-attribute
-    removeTobagoClasses(rendererName);
+  private String aspect;
 
-    addAspectClass(rendererName, Aspect.DEFAULT);
-    if (ComponentUtils.getBooleanAttribute(component, Attributes.DISABLED)) {
-      addAspectClass(rendererName, Aspect.DISABLED);
-    }
-    if (ComponentUtils.getBooleanAttribute(component, Attributes.READONLY)) {
-      addAspectClass(rendererName, Aspect.READONLY);
-    }
-    if (ComponentUtils.getBooleanAttribute(component, Attributes.INLINE)) {
-      addAspectClass(rendererName, Aspect.INLINE);
-    }
-    if (component instanceof UIInput) {
-      UIInput input = (UIInput) component;
-      if (ComponentUtils.isError(input)) {
-        addAspectClass(rendererName, Aspect.ERROR);
-      }
-      if (input.isRequired()) {
-        addAspectClass(rendererName, Aspect.REQUIRED);
-      }
-    }
+  Aspect() {
+    aspect = name().equals("DEFAULT") ? "" : '-' + name().toLowerCase(Locale.ENGLISH);
   }
 
-  private String toRendererName(String rendererType) {
-    return rendererType.substring(0, 1).toLowerCase(Locale.ENGLISH) + rendererType.substring(1);
-  }
-
-  @Override
-  public String toString() {
-    if (classes.isEmpty()) {
-      return null;
-    }
-    StringBuilder buffer = new StringBuilder(16 * classes.size());
-    for (Iterator i = classes.iterator(); i.hasNext();) {
-      String clazz = (String) i.next();
-      buffer.append(clazz);
-      if (i.hasNext()) {
-        buffer.append(' ');
-      }
-    }
-    return buffer.toString();
-  }
-
-  public enum Aspect {
-
-    DEFAULT,
-    DISABLED,
-    READONLY,
-    INLINE,
-    ERROR,
-    REQUIRED;
-
-    private String aspect;
-
-    Aspect() {
-      aspect = name().equals("DEFAULT") ? "" : '-' + name().toLowerCase(Locale.ENGLISH);
-    }
-
-    @Override
-    public String toString() {
-      return aspect;
-    }
+  public String getClassSuffix() {
+    return aspect;
   }
 }

Copied: myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/Classes.java (from r953303, myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/StyleClasses.java)
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/Classes.java?p2=myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/Classes.java&p1=myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/StyleClasses.java&r1=953303&r2=954522&rev=954522&view=diff
==============================================================================
--- myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/StyleClasses.java (original)
+++ myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/Classes.java Mon Jun 14 15:44:55 2010
@@ -1,4 +1,4 @@
-package org.apache.myfaces.tobago.renderkit.html;
+package org.apache.myfaces.tobago.renderkit.css;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
@@ -17,7 +17,8 @@ package org.apache.myfaces.tobago.render
  * limitations under the License.
  */
 
-import org.apache.commons.collections.set.ListOrderedSet;
+import org.apache.commons.collections.map.MultiKeyMap;
+import org.apache.commons.lang.StringUtils;
 import org.apache.myfaces.tobago.component.Attributes;
 import org.apache.myfaces.tobago.component.SupportsMarkup;
 import org.apache.myfaces.tobago.context.Markup;
@@ -31,254 +32,130 @@ import org.slf4j.LoggerFactory;
 import javax.faces.component.UIComponent;
 import javax.faces.component.UIInput;
 import javax.faces.context.FacesContext;
-import java.io.Serializable;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.Map;
+import java.util.EnumSet;
+import java.util.Set;
 
-public class StyleClasses implements Serializable {
-
-  private static final long serialVersionUID = 3738052927067803517L;
-
-  private static final Logger LOG = LoggerFactory.getLogger(StyleClasses.class);
-
-  public static final char SEPARATOR = '-';
-  public static final String PREFIX = "tobago" + SEPARATOR;
-  public static final String MARKUP = SEPARATOR + "markup" + SEPARATOR;
-
-  private ListOrderedSet classes;
-
-  public StyleClasses() {
-    classes = new ListOrderedSet();
-  }
-
-  /**
-   * Creates a StyleClasses element and adds one entry for a sub-component with the name of the renderer.
-   * E. g.: UITreeNode + "icon" -> tobago-treeNode-icon
-   */
-  public StyleClasses(UIComponent component, String sub) {
-    this();
-    addClass(toRendererName(component.getRendererType()), sub);
-  }
-
-  private StyleClasses(StyleClasses base) {
-    this();
-    classes.addAll(base.classes);
-  }
-
-  public static StyleClasses ensureStyleClasses(UIComponent component) {
-    Map attributes = component.getAttributes();
-    StyleClasses classes = (StyleClasses) attributes.get(Attributes.STYLE_CLASS);
-    if (classes == null) {
-      classes = new StyleClasses();
-      attributes.put(Attributes.STYLE_CLASS, classes);
-    }
-    return classes;
-  }
-
-  public static StyleClasses ensureStyleClassesCopy(UIComponent component) {
-    return new StyleClasses(ensureStyleClasses(component));
-  }
-
-  @Deprecated
-  public void addFullQualifiedClass(String clazz) {
-    classes.add(clazz);
-  }
-
-  @Deprecated
-  public void removeFullQualifiedClass(String clazz) {
-    classes.remove(clazz);
-  }
-
-  public void addClass(String renderer, String sub) {
-    classes.add(nameOfClass(renderer, sub));
-  }
-
-  public void removeClass(String renderer, String sub) {
-    classes.remove(nameOfClass(renderer, sub));
-  }
-
-  public boolean isEmpty() {
-    return classes.isEmpty();
-  }
-
-  private String nameOfClass(String renderer, String sub) {
-    StringBuilder builder = new StringBuilder(PREFIX);
-    builder.append(renderer);
-    builder.append(SEPARATOR);
-    builder.append(sub);
-    return builder.toString();
-  }
-
-  public void addMarkupClass(String renderer, String markup) {
-    addMarkupClass(renderer, null, markup);
-  }
-
-  public void removeMarkupClass(String renderer, String markup) {
-    removeMarkupClass(renderer, null, markup);
-  }
-
-  public void addMarkupClass(String renderer, String sub, String markup) {
-    classes.add(nameOfMarkupClass(renderer, sub, markup));
-  }
-
-  public void removeMarkupClass(String renderer, String sub, String markup) {
-    classes.remove(nameOfMarkupClass(renderer, sub, markup));
-  }
-
-  private String nameOfMarkupClass(String renderer, String sub, String markup) {
-    StringBuilder builder = new StringBuilder(PREFIX);
-    builder.append(renderer);
-    if (sub != null) {
-      builder.append(SEPARATOR);
-      builder.append(sub);
-    }
-    builder.append(MARKUP);
-    builder.append(markup);
-    return builder.toString();
-  }
-
-  public void addMarkupClass(UIComponent component, String rendererName) {
-    if (component instanceof SupportsMarkup) {
-      addMarkupClass((SupportsMarkup) component, rendererName, null);
-    }
-  }
-
-  public void addMarkupClass(SupportsMarkup supportsMarkup, String rendererName, String sub) {
-    Markup m = supportsMarkup.getMarkup();
-    if (m != null) {
-      for (String markup : m) {
-        Theme theme = VariableResolverUtils.resolveClientProperties(FacesContext.getCurrentInstance()).getTheme();
-        if (theme.getRenderersConfig().isMarkupSupported(rendererName, markup)) {
-          addMarkupClass(rendererName, sub, markup);
-        } else if ("none".equals(markup)) {
-          Deprecation.LOG.warn("Markup 'none' is deprecated, please use a NULL pointer instead.");
-        } else {
-          LOG.warn("Unknown markup='" + markup + "'");
-        }
-      }
-    }
-  }
+/**
+ * Builds the CSS class attribute of tags.
+ * The names will be generated in a formal way, so generic name (and abbrevation) are possible.
+ * The class works like a factory, so caching will be possible.
+ */
+public final class Classes {
 
-  public void addAspectClass(String renderer, Aspect aspect) {
-    classes.add(nameOfAspectClass(renderer, aspect));
-  }
+  private static final Logger LOG = LoggerFactory.getLogger(Classes.class);
 
-  public void removeAspectClass(String renderer, Aspect aspect) {
-    classes.remove(nameOfAspectClass(renderer, aspect));
-  }
-
-  private String nameOfAspectClass(String renderer, Aspect aspect) {
-    StringBuilder builder = new StringBuilder(PREFIX);
-    builder.append(renderer);
-    builder.append(aspect);
-    return builder.toString();
-  }
+  private static final MultiKeyMap CACHE = new MultiKeyMap();
 
-  public void addAspectClass(String renderer, String sub, Aspect aspect) {
-    classes.add(nameOfAspectClass(renderer, sub, aspect));
-  }
+  private final String stringValue;
 
-  public void removeAspectClass(String renderer, String sub, Aspect aspect) {
-    classes.remove(nameOfAspectClass(renderer, sub, aspect));
+  public static Classes simple(UIComponent component, String sub) {
+    return create(component, false, null, sub);
   }
 
-  private String nameOfAspectClass(String renderer, String sub, Aspect aspect) {
-    StringBuilder builder = new StringBuilder(PREFIX);
-    builder.append(renderer);
-    builder.append(SEPARATOR);
-    builder.append(sub);
-    builder.append(aspect);
-    return builder.toString();
+  public static Classes full(UIComponent component) {
+    return create(component, true, null, null);
   }
 
-  public void addClasses(StyleClasses styleClasses) {
-    for (String clazz : (Iterable<String>) styleClasses.classes) {
-      classes.add(clazz);
-    }
+  public static Classes full(UIComponent component, String sub) {
+    return create(component, true, null, sub);
   }
 
-  public void removeClass(String clazz) {
-    classes.remove(clazz);
+  public static Classes full(UIComponent component, String[] subs) {
+    return create(component, true, subs, null);
   }
 
-  public void removeTobagoClasses(String rendererName) {
-    for (Iterator i = classes.iterator(); i.hasNext();) {
-      String clazz = (String) i.next();
-      if (clazz.startsWith(PREFIX + rendererName)) {
-        i.remove();
-      }
+  private static Classes create(UIComponent component, boolean full, String[] subs, String sub) {
+    final Object s = sub != null ? sub : subs;
+    final String rendererName = StringUtils.uncapitalize(component.getRendererType());
+    final Set<Aspect> aspects = full ? evaluateAspects(component) : null;
+    final Markup markup = full ? ((SupportsMarkup) component).getMarkup() : null;
+    Classes value = (Classes) CACHE.get(rendererName, aspects, markup, s);
+    if (value == null) {
+      value = new Classes(rendererName, aspects, markup, subs, sub);
+      CACHE.put(rendererName, aspects, markup, s, value);
+      LOG.info("Classes cache size = " + CACHE.size());
     }
+    return value;
   }
 
-  public void updateClassAttributeAndMarkup(UIComponent component, String rendererName) {
-    updateClassAttribute(component, rendererName);
-    addMarkupClass(component, rendererName);
+  private Classes(String rendererName, Set<Aspect> aspects, Markup markup, String[] subs, String sub) {
+    this.stringValue = encode(rendererName, aspects, markup, subs, sub);
   }
 
-  public void updateClassAttribute(UIComponent component, String rendererName) {
-    // first remove old tobago-<rendererName>-<type> classes from class-attribute
-    removeTobagoClasses(rendererName);
-
-    addAspectClass(rendererName, Aspect.DEFAULT);
+  private static Set<Aspect> evaluateAspects(UIComponent component) {
+    Set<Aspect> aspects = EnumSet.noneOf(Aspect.class);
+    aspects.add(Aspect.DEFAULT);
     if (ComponentUtils.getBooleanAttribute(component, Attributes.DISABLED)) {
-      addAspectClass(rendererName, Aspect.DISABLED);
+      aspects.add(Aspect.DISABLED);
     }
     if (ComponentUtils.getBooleanAttribute(component, Attributes.READONLY)) {
-      addAspectClass(rendererName, Aspect.READONLY);
+      aspects.add(Aspect.READONLY);
     }
     if (ComponentUtils.getBooleanAttribute(component, Attributes.INLINE)) {
-      addAspectClass(rendererName, Aspect.INLINE);
+      aspects.add(Aspect.INLINE);
     }
     if (component instanceof UIInput) {
       UIInput input = (UIInput) component;
       if (ComponentUtils.isError(input)) {
-        addAspectClass(rendererName, Aspect.ERROR);
+        aspects.add(Aspect.ERROR);
       }
       if (input.isRequired()) {
-        addAspectClass(rendererName, Aspect.REQUIRED);
+        aspects.add(Aspect.REQUIRED);
       }
     }
+    return aspects;
   }
 
-  private String toRendererName(String rendererType) {
-    return rendererType.substring(0, 1).toLowerCase(Locale.ENGLISH) + rendererType.substring(1);
-  }
+  private String encode(String rendererName, Set<Aspect> aspects, Markup markup, String[] subs, String sub) {
+    StringBuilder builder = new StringBuilder();
+    if (aspects != null) {
+      for (Aspect aspect : aspects) {
+        builder.append("tobago-");
+        builder.append(rendererName);
+        builder.append(aspect.getClassSuffix());
+        builder.append(' ');
+      }
+    }
+    if (markup != null) {
+      for (String markupString : markup) {
+        Theme theme = VariableResolverUtils.resolveClientProperties(FacesContext.getCurrentInstance()).getTheme();
+        if (theme.getRenderersConfig().isMarkupSupported(rendererName, markupString)) {
+          builder.append("tobago-");
+          builder.append(rendererName);
+          builder.append("-markup-");
+          builder.append(markupString);
+          builder.append(' ');
+        } else if ("none".equals(markupString)) {
+          Deprecation.LOG.warn("Markup 'none' is deprecated, please use a NULL pointer instead.");
+        } else {
+          LOG.warn("Unknown markup='" + markupString + "'");
+        }
+      }
 
-  @Override
-  public String toString() {
-    if (classes.isEmpty()) {
-      return null;
     }
-    StringBuilder buffer = new StringBuilder(16 * classes.size());
-    for (Iterator i = classes.iterator(); i.hasNext();) {
-      String clazz = (String) i.next();
-      buffer.append(clazz);
-      if (i.hasNext()) {
-        buffer.append(' ');
+    if (subs != null) {
+      for (String subComponent : subs) {
+        builder.append("tobago-");
+        builder.append(rendererName);
+        builder.append("-");
+        builder.append(subComponent);
+        builder.append(' ');
       }
     }
-    return buffer.toString();
-  }
-
-  public enum Aspect {
-
-    DEFAULT,
-    DISABLED,
-    READONLY,
-    INLINE,
-    ERROR,
-    REQUIRED;
-
-    private String aspect;
-
-    Aspect() {
-      aspect = name().equals("DEFAULT") ? "" : '-' + name().toLowerCase(Locale.ENGLISH);
+    if (sub != null) {
+      builder.append("tobago-");
+      builder.append(rendererName);
+      builder.append("-");
+      builder.append(sub);
+      builder.append(' ');
     }
-
-    @Override
-    public String toString() {
-      return aspect;
+    if (builder.length() > 0) {
+      builder.setLength(builder.length() - 1);
     }
+    return builder.toString();
+  }
+
+  public String getStringValue() {
+    return stringValue;
   }
 }

Modified: myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/StyleClasses.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/StyleClasses.java?rev=954522&r1=954521&r2=954522&view=diff
==============================================================================
--- myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/StyleClasses.java (original)
+++ myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/StyleClasses.java Mon Jun 14 15:44:55 2010
@@ -37,6 +37,10 @@ import java.util.Iterator;
 import java.util.Locale;
 import java.util.Map;
 
+/**
+ * @deprecated since Tobago 1.5.0. Please use {@link org.apache.myfaces.tobago.renderkit.css.Classes}.
+ */
+@Deprecated
 public class StyleClasses implements Serializable {
 
   private static final long serialVersionUID = 3738052927067803517L;
@@ -258,6 +262,10 @@ public class StyleClasses implements Ser
     return buffer.toString();
   }
 
+  /**
+   * @deprecated since Tobago 1.5.0. Please use {@link org.apache.myfaces.tobago.renderkit.css.Aspect}.
+   */
+  @Deprecated
   public enum Aspect {
 
     DEFAULT,

Modified: myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/TobagoResponseWriter.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/TobagoResponseWriter.java?rev=954522&r1=954521&r2=954522&view=diff
==============================================================================
--- myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/TobagoResponseWriter.java (original)
+++ myfaces/tobago/trunk/tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/TobagoResponseWriter.java Mon Jun 14 15:44:55 2010
@@ -17,6 +17,7 @@ package org.apache.myfaces.tobago.webapp
  * limitations under the License.
  */
 
+import org.apache.myfaces.tobago.renderkit.css.Classes;
 import org.apache.myfaces.tobago.renderkit.css.Style;
 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlConstants;
@@ -136,6 +137,13 @@ public abstract class TobagoResponseWrit
   /**
    * Write the class attribute. The value will not escaped.
    */
+  public void writeClassAttribute(Classes classes) throws IOException {
+    writeAttribute(HtmlAttributes.CLASS, classes.getStringValue(), false);
+  }
+
+  /**
+   * Write the class attribute. The value will not escaped.
+   */
   public abstract void writeClassAttribute() throws IOException;
 
   /**

Added: myfaces/tobago/trunk/tobago-core/src/test/java/org/apache/myfaces/tobago/renderkit/css/ClassesUnitTest.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/tobago-core/src/test/java/org/apache/myfaces/tobago/renderkit/css/ClassesUnitTest.java?rev=954522&view=auto
==============================================================================
--- myfaces/tobago/trunk/tobago-core/src/test/java/org/apache/myfaces/tobago/renderkit/css/ClassesUnitTest.java (added)
+++ myfaces/tobago/trunk/tobago-core/src/test/java/org/apache/myfaces/tobago/renderkit/css/ClassesUnitTest.java Mon Jun 14 15:44:55 2010
@@ -0,0 +1,114 @@
+package org.apache.myfaces.tobago.renderkit.css;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+import org.apache.myfaces.tobago.component.ComponentTypes;
+import org.apache.myfaces.tobago.component.CreateComponentUtils;
+import org.apache.myfaces.tobago.component.RendererTypes;
+import org.apache.myfaces.tobago.component.UIInput;
+import org.apache.myfaces.tobago.context.Markup;
+import org.apache.myfaces.tobago.internal.mock.faces.AbstractTobagoTestBase;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ClassesUnitTest extends AbstractTobagoTestBase {
+
+  @Test
+  public void testSimple() {
+    final UIInput in = (UIInput) CreateComponentUtils.createComponent(
+        getFacesContext(), ComponentTypes.IN, RendererTypes.IN, "in");
+    Assert.assertEquals("tobago-in-simple", Classes.simple(in, "simple").getStringValue());
+  }
+
+  @Test
+  public void testFull() {
+    final UIInput in = (UIInput) CreateComponentUtils.createComponent(
+        getFacesContext(), ComponentTypes.IN, RendererTypes.IN, "in");
+    Assert.assertEquals("tobago-in", Classes.full(in).getStringValue());
+  }
+
+  @Test
+  public void testDisabled() {
+    final UIInput in = (UIInput) CreateComponentUtils.createComponent(
+        getFacesContext(), ComponentTypes.IN, RendererTypes.IN, "in");
+    in.setDisabled(true);
+    Assert.assertEquals("tobago-in tobago-in-disabled", Classes.full(in).getStringValue());
+  }
+
+  @Test
+  public void testReadonly() {
+    final UIInput in = (UIInput) CreateComponentUtils.createComponent(
+        getFacesContext(), ComponentTypes.IN, RendererTypes.IN, "in");
+    in.setReadonly(true);
+    Assert.assertEquals("tobago-in tobago-in-readonly", Classes.full(in).getStringValue());
+  }
+
+  @Test
+  public void testDisabledReadonly() {
+    final UIInput in = (UIInput) CreateComponentUtils.createComponent(
+        getFacesContext(), ComponentTypes.IN, RendererTypes.IN, "in");
+    in.setDisabled(true);
+    in.setReadonly(true);
+    Assert.assertEquals("tobago-in tobago-in-disabled tobago-in-readonly", Classes.full(in).getStringValue());
+  }
+
+  @Test
+  public void testError() {
+    final UIInput in = (UIInput) CreateComponentUtils.createComponent(
+        getFacesContext(), ComponentTypes.IN, RendererTypes.IN, "in");
+    in.setValid(false);
+    Assert.assertEquals("tobago-in tobago-in-error", Classes.full(in).getStringValue());
+  }
+
+  @Test
+  public void testMarkup() {
+    final UIInput in = (UIInput) CreateComponentUtils.createComponent(
+        getFacesContext(), ComponentTypes.IN, RendererTypes.IN, "in");
+    in.setMarkup(Markup.valueOf("important"));
+    Assert.assertEquals("tobago-in tobago-in-markup-important", Classes.full(in).getStringValue());
+  }
+
+  @Test
+  public void testSub() {
+    final UIInput in = (UIInput) CreateComponentUtils.createComponent(
+        getFacesContext(), ComponentTypes.IN, RendererTypes.IN, "in");
+    Assert.assertEquals("tobago-in tobago-in-sub", Classes.full(in, "sub").getStringValue());
+  }
+
+  @Test
+  public void testSubs() {
+    final UIInput in = (UIInput) CreateComponentUtils.createComponent(
+        getFacesContext(), ComponentTypes.IN, RendererTypes.IN, "in");
+    Assert.assertEquals("tobago-in tobago-in-sub1 tobago-in-sub2",
+        Classes.full(in, new String[]{"sub1", "sub2"}).getStringValue());
+  }
+
+  @Test
+  public void testMixed() {
+    final UIInput in = (UIInput) CreateComponentUtils.createComponent(
+        getFacesContext(), ComponentTypes.IN, RendererTypes.IN, "in");
+    in.setDisabled(true);
+    in.setReadonly(true);
+    in.setValid(false);
+    in.setMarkup(Markup.valueOf("important,deleted"));
+    Assert.assertEquals("tobago-in tobago-in-disabled tobago-in-readonly tobago-in-error "
+        + "tobago-in-markup-important tobago-in-markup-deleted tobago-in-sub",
+        Classes.full(in, "sub").getStringValue());
+  }
+
+}

Modified: myfaces/tobago/trunk/tobago-core/src/test/java/org/apache/myfaces/tobago/renderkit/html/StyleClassesUnitTest.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/tobago-core/src/test/java/org/apache/myfaces/tobago/renderkit/html/StyleClassesUnitTest.java?rev=954522&r1=954521&r2=954522&view=diff
==============================================================================
--- myfaces/tobago/trunk/tobago-core/src/test/java/org/apache/myfaces/tobago/renderkit/html/StyleClassesUnitTest.java (original)
+++ myfaces/tobago/trunk/tobago-core/src/test/java/org/apache/myfaces/tobago/renderkit/html/StyleClassesUnitTest.java Mon Jun 14 15:44:55 2010
@@ -17,68 +17,72 @@ package org.apache.myfaces.tobago.render
  * limitations under the License.
  */
 
-import junit.framework.TestCase;
+import org.junit.Assert;
+import org.junit.Test;
 
-/*
- * Date: 2007-05-01
- */
-
-public class StyleClassesUnitTest extends TestCase {
+public class StyleClassesUnitTest {
 
+  @Test
   public void testRemoveOneMatch() {
     StyleClasses c = new StyleClasses();
     c.addFullQualifiedClass("bla");
     c.addFullQualifiedClass("tobago-test-inline");
     c.addFullQualifiedClass("blupp");
     c.removeTobagoClasses("test");
-    assertEquals("bla blupp", c.toString());
+    Assert.assertEquals("bla blupp", c.toString());
   }
 
+  @Test
   public void testRemoveNoMatch() {
     StyleClasses c = new StyleClasses();
     c.addFullQualifiedClass("bla");
     c.addFullQualifiedClass("tobago-test-inline");
     c.addFullQualifiedClass("blupp");
     c.removeTobagoClasses("no");
-    assertEquals("bla tobago-test-inline blupp", c.toString());
+    Assert.assertEquals("bla tobago-test-inline blupp", c.toString());
   }
 
+  @Test
   public void testRemoveEmpty() {
     StyleClasses c = new StyleClasses();
     c.removeTobagoClasses("no");
-    assertEquals(null, c.toString());
+    Assert.assertEquals(null, c.toString());
   }
 
+  @Test
   public void testAddMarkupClass() {
     StyleClasses c = new StyleClasses();
     c.addMarkupClass("myComponent", "big");
-    assertEquals("tobago-myComponent-markup-big", c.toString());
+    Assert.assertEquals("tobago-myComponent-markup-big", c.toString());
     c.removeMarkupClass("myComponent", "big");
-    assertEquals(null, c.toString());
+    Assert.assertEquals(null, c.toString());
   }
 
+  @Test
   public void testAddMarkupClassSub() {
     StyleClasses c = new StyleClasses();
     c.addMarkupClass("myComponent", "mySub", "big");
-    assertEquals("tobago-myComponent-mySub-markup-big", c.toString());
+    Assert.assertEquals("tobago-myComponent-mySub-markup-big", c.toString());
     c.removeMarkupClass("myComponent", "mySub", "big");
-    assertEquals(null, c.toString());
+    Assert.assertEquals(null, c.toString());
   }
 
+  @Test
   public void testAddAspectClass() {
     StyleClasses c = new StyleClasses();
     c.addAspectClass("myComponent", StyleClasses.Aspect.DISABLED);
-    assertEquals("tobago-myComponent-disabled", c.toString());
+    Assert.assertEquals("tobago-myComponent-disabled", c.toString());
     c.removeAspectClass("myComponent", StyleClasses.Aspect.DISABLED);
-    assertEquals(null, c.toString());
+    Assert.assertEquals(null, c.toString());
   }
 
+  @Test
   public void testAddAspectClassSub() {
     StyleClasses c = new StyleClasses();
     c.addAspectClass("myComponent", "mySub", StyleClasses.Aspect.DISABLED);
-    assertEquals("tobago-myComponent-mySub-disabled", c.toString());
+    Assert.assertEquals("tobago-myComponent-mySub-disabled", c.toString());
     c.removeAspectClass("myComponent", "mySub", StyleClasses.Aspect.DISABLED);
-    assertEquals(null, c.toString());
+    Assert.assertEquals(null, c.toString());
   }
 
 }

Modified: myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/java/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/tag/TreeNodeRenderer.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/java/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/tag/TreeNodeRenderer.java?rev=954522&r1=954521&r2=954522&view=diff
==============================================================================
--- myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/java/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/tag/TreeNodeRenderer.java (original)
+++ myfaces/tobago/trunk/tobago-theme/tobago-theme-scarborough/src/main/java/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/tag/TreeNodeRenderer.java Mon Jun 14 15:44:55 2010
@@ -34,10 +34,10 @@ import org.apache.myfaces.tobago.layout.
 import org.apache.myfaces.tobago.layout.Measure;
 import org.apache.myfaces.tobago.model.TreeState;
 import org.apache.myfaces.tobago.renderkit.CommandRendererBase;
+import org.apache.myfaces.tobago.renderkit.css.Classes;
 import org.apache.myfaces.tobago.renderkit.css.Style;
 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlConstants;
-import org.apache.myfaces.tobago.renderkit.html.StyleClasses;
 import org.apache.myfaces.tobago.renderkit.html.util.CommandRendererHelper;
 import org.apache.myfaces.tobago.renderkit.html.util.HtmlRendererUtils;
 import org.apache.myfaces.tobago.renderkit.util.RenderUtils;
@@ -145,9 +145,7 @@ public class TreeNodeRenderer extends Co
       writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
       if (!alreadyExists) {
         writer.startElement(HtmlConstants.DIV, null);
-        StyleClasses levelClass = new StyleClasses();
-        levelClass.addClass("treeListbox", "level");
-        writer.writeClassAttribute(levelClass);
+        writer.writeClassAttribute(Classes.simple(tree, "level"));
         Style levelStyle = new Style();
         levelStyle.setLeft(Measure.valueOf(level * 160)); // xxx 160 should be configurable
         writer.writeStyleAttribute(levelStyle);
@@ -157,18 +155,14 @@ public class TreeNodeRenderer extends Co
           writer.startElement(HtmlConstants.SELECT, null);
           writer.writeAttribute(HtmlAttributes.DISABLED, true);
           writer.writeAttribute(HtmlAttributes.SIZE, 2); // must be > 1, but the size comes from the layout
-          StyleClasses selectClass = new StyleClasses();
-          selectClass.addClass("treeListbox", "select");
-          writer.writeClassAttribute(selectClass);
+          writer.writeClassAttribute(Classes.simple(tree, "select"));
           writer.endElement(HtmlConstants.SELECT);
         }
       }
 
       writer.startElement(HtmlConstants.SELECT, node);
       writer.writeIdAttribute(id + ComponentUtils.SUB_SEPARATOR + "select");
-      StyleClasses selectClass = new StyleClasses();
-      selectClass.addClass("treeListbox", "select");
-      writer.writeClassAttribute(selectClass);
+      writer.writeClassAttribute(Classes.simple(tree, "select"));
       if (!expanded) {
         Style selectStyle = new Style();
         selectStyle.setDisplay(Display.NONE);
@@ -194,8 +188,8 @@ public class TreeNodeRenderer extends Co
     final boolean hasNextSibling = node.isHasNextSibling();
     final List<Boolean> junctions = node.getJunctions();
 
-    final boolean expanded = isExpanded(tree, node);
     final boolean showRoot = tree.isShowRoot();
+    final boolean expanded = isExpanded(tree, node) || !showRoot && level == 0;
     final boolean showIcons;
     final boolean showJunctions;
     final boolean showRootJunction;
@@ -257,16 +251,17 @@ public class TreeNodeRenderer extends Co
       }
 
       // div class (css)
-      StyleClasses styleClasses = StyleClasses.ensureStyleClasses(node);
-      styleClasses.updateClassAttributeAndMarkup(node, "treeNode");
+      final Classes classes;
       if (isMenu) {
-        styleClasses.addClass("treeNode", "menu");
         if (marked) {
-          styleClasses.addClass("treeNode", "marker");
+          classes = Classes.full(node, new String[] {"menu", "marker"});
+        } else {
+          classes = Classes.full(node, "menu");
         }
+      } else {
+        classes = Classes.full(node);
       }
-      styleClasses.addMarkupClass(node, "treeNode");
-      writer.writeClassAttribute(styleClasses);
+      writer.writeClassAttribute(classes);
 
       // div style (width)
       Style style = new Style(facesContext, tree);
@@ -286,9 +281,9 @@ public class TreeNodeRenderer extends Co
         encodeMenuIcon(facesContext, writer, treeId, id, expanded, node);
       }
 
-      encodeIndent(facesContext, writer, isMenu, showJunctions, junctions);
+      encodeIndent(facesContext, writer, node, isMenu, showJunctions, junctions);
 
-      encodeTreeJunction(facesContext, writer, id, treeId, showJunctions, showRootJunction, showRoot, expanded,
+      encodeTreeJunction(facesContext, writer, node, id, treeId, showJunctions, showRootJunction, showRoot, expanded,
           folder, level, hasNextSibling, openSource, closedSource);
 
       encodeTreeIcons(writer, node, id, treeId, showIcons, folder, source, openSource, closedSource);
@@ -335,7 +330,7 @@ public class TreeNodeRenderer extends Co
     }
     String src = expanded ? menuOpen : menuClose;
     writer.startElement(HtmlConstants.IMG, null);
-    writer.writeClassAttribute(new StyleClasses(node, "menuIcon"));
+    writer.writeClassAttribute(Classes.simple(node, "menuIcon"));
     writer.writeIdAttribute(id + "-menuIcon");
     writer.writeAttribute("src", src, true);
     writer.writeAttribute("onclick", onclick, true);
@@ -344,7 +339,7 @@ public class TreeNodeRenderer extends Co
   }
 
   private void encodeIndent(
-      final FacesContext facesContext, final TobagoResponseWriter writer, final boolean menuMode,
+      final FacesContext facesContext, final TobagoResponseWriter writer, final UITreeNode node, final boolean menuMode,
       final boolean showJunctions, final List<Boolean> junctions)
       throws IOException {
 
@@ -353,7 +348,7 @@ public class TreeNodeRenderer extends Co
 
     for (Boolean junction : junctions) {
       writer.startElement(HtmlConstants.IMG, null);
-      writer.writeClassAttribute("tobago-treeNode-junction");
+      writer.writeClassAttribute(Classes.simple(node, "junction"));
       if (junction && !menuMode && showJunctions) {
         writer.writeAttribute("src", perpendicular, true);
       } else {
@@ -364,7 +359,7 @@ public class TreeNodeRenderer extends Co
   }
 
   private void encodeTreeJunction(
-      FacesContext facesContext, TobagoResponseWriter writer, String id, String treeId,
+      FacesContext facesContext, TobagoResponseWriter writer, UITreeNode node, String id, String treeId,
       boolean showJunctions, boolean showRootJunction, boolean showRoot, boolean expanded, boolean folder,
       int level, boolean hasNextSibling, String openSource, String closedSource)
       throws IOException {
@@ -372,7 +367,7 @@ public class TreeNodeRenderer extends Co
         || !showRootJunction && level == 0
         || !showRootJunction && !showRoot && level == 1)) {
       writer.startElement(HtmlConstants.IMG, null);
-      writer.writeClassAttribute("tobago-treeNode-junction");
+      writer.writeClassAttribute(Classes.simple(node, "junction"));
       writer.writeIdAttribute(id + "-junction");
 
       String gif = folder && expanded
@@ -403,7 +398,7 @@ public class TreeNodeRenderer extends Co
 
     if (showIcons) {
       writer.startElement(HtmlConstants.IMG, null);
-      writer.writeClassAttribute(new StyleClasses(node, "icon"));
+      writer.writeClassAttribute(Classes.simple(node, "icon"));
       writer.writeIdAttribute(id + "-icon"); //XXX may not okay with naming conventions
 
       writer.writeAttribute("src", source, true);
@@ -453,7 +448,7 @@ public class TreeNodeRenderer extends Co
       }
     }
     if (marked) {
-      writer.writeClassAttribute(new StyleClasses(node, "marker"));
+      writer.writeClassAttribute(Classes.simple(node, "marker"));
     }
     String tip = node.getTip();
     if (tip != null) {