View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.tobago.renderkit.css;
21  
22  import org.apache.commons.collections.map.MultiKeyMap;
23  import org.apache.myfaces.tobago.component.Visual;
24  import org.apache.myfaces.tobago.context.ClientProperties;
25  import org.apache.myfaces.tobago.context.Markup;
26  import org.apache.myfaces.tobago.context.Theme;
27  import org.apache.myfaces.tobago.internal.util.Deprecation;
28  import org.apache.myfaces.tobago.internal.util.StringUtils;
29  import org.apache.myfaces.tobago.util.ComponentUtils;
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  
33  import javax.faces.component.UIComponent;
34  import javax.faces.context.FacesContext;
35  
36  /**
37   * <p>
38   * Builds the CSS class attribute of tags. The names will be generated in a formal way, so generic name (and
39   * abbreviation) are possible. The class works like a factory, so caching will be possible.
40   * </p>
41   * <p>
42   * The default naming conventions allow these values:
43   * </p>
44   * <ul>
45   *   <li>tobago-&lt;rendererName&gt;
46   *   <li>tobago-&lt;rendererName&gt;-markup-&lt;markupName&gt;
47   *   <li>tobago-&lt;rendererName&gt;-&lt;subElement&gt;
48   *   <li>tobago-&lt;rendererName&gt;-&lt;subElement&gt;-markup-&lt;markupName&gt;
49   * </ul>
50   * <br>
51   * where <ul> <li>&lt;rendererName&gt;, &lt;subElement&gt; and &lt;markupName&gt;
52   * must only contain ASCII-chars and -numbers</li>
53   * <li>&lt;rendererName&gt; is the rendererType with a lower case char as first char</li> <li>&lt;subElement&gt; is a
54   * sub element of the main tag in the output language (e.g. HTML)</li> <li>&lt;markupName&gt; is the name of an existing
55   * markup</li> </ul> If the markup contains more than one name, there will be generated more than one output string.
56   * E.g.: UIIn with Markup [readonly, error] will get the class "tobago-in tobago-in-markup-readonly
57   * tobago-in-markup-error".
58   *
59   * @deprecated since Tobago 3.0.0
60   */
61  @Deprecated
62  public final class Classes implements CssItem {
63  
64    private static final Logger LOG = LoggerFactory.getLogger(Classes.class);
65  
66    /* With cache it seems to be 10 times faster */
67    private static final MultiKeyMap CACHE = new MultiKeyMap();
68  
69    private final String stringValue;
70  
71    public static Classes create(final UIComponent component) {
72      return create(component, true, null, null, false);
73    }
74  
75    public static Classes create(final UIComponent component, final String sub) {
76      return create(component, true, sub, null, false);
77    }
78  
79    public static Classes create(final UIComponent component, final Markup explicit) {
80      return create(component, true, null, explicit, false);
81    }
82  
83    // TODO: clean up..., hope this is no longer needed...
84    public static Classes create(final UIComponent component, final String sub, final Markup explicit) {
85      return create(component, false, sub, explicit, false);
86    }
87  
88    // XXX optimize synchronized
89    private static synchronized Classes create(
90        final UIComponent component, final boolean markupFromComponent, final String sub,
91        final Markup explicit, final boolean ignoreCheck) {
92      final String rendererName = StringUtils.uncapitalize(component.getRendererType());
93  
94      Markup markup;
95      if (markupFromComponent) {
96        final Visual visual = (Visual) component;
97        markup = ComponentUtils.updateMarkup(component, visual.getMarkup());
98        if (explicit != null) {
99          markup = explicit.add(markup);
100       }
101     } else {
102       markup = explicit;
103     }
104 
105 //    Classes value = (Classes) CACHE.get(rendererName, markup, sub);
106 //    if (value == null) {
107     final Classes value;
108     value = new Classes(rendererName, markup, sub, ignoreCheck);
109 //      CACHE.put(rendererName, markup, sub, value);
110 //      if (LOG.isDebugEnabled()) {
111 //        LOG.debug("Element added (size={}) to cache (renderName='{}', markup='{}', sub='{}')",
112 //            CACHE.size(), rendererName, markup, sub);
113 //      }
114 //    }
115     return value;
116   }
117   private Classes(final String rendererName, final Markup markup, final String sub, final boolean ignoreMarkupCheck) {
118 
119     assert sub == null || StringUtils.isAlphanumeric(sub) : "Invalid sub element name: '" + sub + "'";
120 
121     // These values are statistically tested length of the html class attribute
122     final StringBuilder builder = new StringBuilder(markup != null ? 80 : 32);
123     builder.append("tobago-");
124     builder.append(rendererName);
125     if (sub != null) {
126       builder.append('-');
127       builder.append(sub);
128     }
129     if (markup != null) {
130       final Theme theme = ClientProperties.getInstance(FacesContext.getCurrentInstance()).getTheme();
131       for (final String markupString : markup) {
132         if (ignoreMarkupCheck || theme.getRenderersConfig().isMarkupSupported(rendererName, markupString)) {
133           builder.append(' ');
134           builder.append("tobago-");
135           builder.append(rendererName);
136           if (sub != null) {
137             builder.append('-');
138             builder.append(sub);
139           }
140           builder.append("-markup-");
141           builder.append(markupString);
142         } else if ("none".equals(markupString)) {
143           Deprecation.LOG.warn("Markup 'none' is deprecated, please use a NULL pointer instead.");
144         } else {
145           LOG.warn("Ignoring unknown markup='" + markupString + "' for rendererName='" + rendererName + "'");
146         }
147       }
148     }
149     this.stringValue = builder.toString();
150   }
151 
152   public String getName() {
153     return stringValue;
154   }
155 }