001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *   http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    
020    package org.apache.myfaces.tobago.renderkit.html;
021    
022    import org.apache.commons.collections.set.ListOrderedSet;
023    import org.apache.commons.lang.StringUtils;
024    import org.apache.commons.logging.Log;
025    import org.apache.commons.logging.LogFactory;
026    import org.apache.myfaces.tobago.component.ComponentUtil;
027    import org.apache.myfaces.tobago.component.SupportsMarkup;
028    import org.apache.myfaces.tobago.context.ClientProperties;
029    import org.apache.myfaces.tobago.context.Theme;
030    
031    import javax.faces.application.FacesMessage;
032    import javax.faces.component.UIComponent;
033    import javax.faces.component.UIInput;
034    import javax.faces.context.FacesContext;
035    import java.io.Serializable;
036    import java.util.Iterator;
037    import java.util.Locale;
038    import java.util.Map;
039    
040    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_DISABLED;
041    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_INLINE;
042    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_READONLY;
043    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_STYLE_CLASS;
044    
045    public class StyleClasses implements Serializable {
046    
047      private static final long serialVersionUID = 3738052927067803517L;
048    
049      private static final Log LOG = LogFactory.getLog(StyleClasses.class);
050    
051      public static final char SEPERATOR = '-';
052      public static final String PREFIX = "tobago" + SEPERATOR;
053      public static final String MARKUP = SEPERATOR + "markup" + SEPERATOR;
054    
055      private ListOrderedSet classes;
056    
057      public StyleClasses() {
058        classes = new ListOrderedSet();
059      }
060    
061      private StyleClasses(StyleClasses base) {
062        this();
063        classes.addAll(base.classes);
064      }
065    
066      public static StyleClasses ensureStyleClasses(UIComponent component) {
067        Map attributes = component.getAttributes();
068        StyleClasses classes = (StyleClasses) attributes.get(ATTR_STYLE_CLASS);
069        if (classes == null) {
070          classes = new StyleClasses();
071          attributes.put(ATTR_STYLE_CLASS, classes);
072        }
073        return classes;
074      }
075    
076      public static StyleClasses ensureStyleClassesCopy(UIComponent component) {
077        return new StyleClasses(ensureStyleClasses(component));
078      }
079    
080      @Deprecated
081      public void addFullQualifiedClass(String clazz) {
082        classes.add(clazz);
083      }
084    
085      @Deprecated
086      public void removeFullQualifiedClass(String clazz) {
087        classes.remove(clazz);
088      }
089    
090      public void addClass(String renderer, String sub) {
091        classes.add(nameOfClass(renderer, sub));
092      }
093    
094      public void removeClass(String renderer, String sub) {
095        classes.remove(nameOfClass(renderer, sub));
096      }
097    
098      private String nameOfClass(String renderer, String sub) {
099        StringBuilder builder = new StringBuilder(PREFIX);
100        builder.append(renderer);
101        builder.append(SEPERATOR);
102        builder.append(sub);
103        return builder.toString();
104      }
105    
106      public void addMarkupClass(String renderer, String markup) {
107        addMarkupClass(renderer, null, markup);
108      }
109    
110      public void removeMarkupClass(String renderer, String markup) {
111        removeMarkupClass(renderer, null, markup);
112      }
113    
114      public void addMarkupClass(String renderer, String sub, String markup) {
115        classes.add(nameOfMarkupClass(renderer, sub, markup));
116      }
117    
118      public void removeMarkupClass(String renderer, String sub, String markup) {
119        classes.remove(nameOfMarkupClass(renderer, sub, markup));
120      }
121    
122      private String nameOfMarkupClass(String renderer, String sub, String markup) {
123        StringBuilder builder = new StringBuilder(PREFIX);
124        builder.append(renderer);
125        if (sub != null) {
126          builder.append(SEPERATOR);
127          builder.append(sub);
128        }
129        builder.append(MARKUP);
130        builder.append(markup);
131        return builder.toString();
132      }
133    
134      public void addMarkupClass(UIComponent component, String rendererName) {
135        if (component instanceof SupportsMarkup) {
136          addMarkupClass((SupportsMarkup) component, rendererName, null);
137        }
138      }
139    
140      public void addMarkupClass(SupportsMarkup supportsMarkup, String rendererName, String sub) {
141        for (String markup : supportsMarkup.getMarkup()) {
142          if (!StringUtils.isBlank(markup)) {
143            Theme theme = ClientProperties.getInstance(FacesContext.getCurrentInstance().getViewRoot()).getTheme();
144            if (theme.getRenderersConfig().isMarkupSupported(rendererName, markup)) {
145              addMarkupClass(rendererName, sub, markup);
146            } else if (!"none".equals(markup)) {
147              LOG.warn("Ignoring unknown markup='" + markup + "' for rendererName='" + rendererName + "'");
148            }
149          }
150        }
151      }
152    
153      public void addAspectClass(String renderer, Aspect aspect) {
154        classes.add(nameOfAspectClass(renderer, aspect));
155      }
156    
157      public void removeAspectClass(String renderer, Aspect aspect) {
158        classes.remove(nameOfAspectClass(renderer, aspect));
159      }
160    
161      private String nameOfAspectClass(String renderer, Aspect aspect) {
162        StringBuilder builder = new StringBuilder(PREFIX);
163        builder.append(renderer);
164        builder.append(aspect);
165        return builder.toString();
166      }
167    
168    
169      public void addAspectClass(String renderer, String sub, Aspect aspect) {
170        classes.add(nameOfAspectClass(renderer, sub, aspect));
171      }
172    
173      public void removeAspectClass(String renderer, String sub, Aspect aspect) {
174        classes.remove(nameOfAspectClass(renderer, sub, aspect));
175      }
176    
177      private String nameOfAspectClass(String renderer, String sub, Aspect aspect) {
178        StringBuilder builder = new StringBuilder(PREFIX);
179        builder.append(renderer);
180        builder.append(SEPERATOR);
181        builder.append(sub);
182        builder.append(aspect);
183        return builder.toString();
184      }
185    
186      public void addClasses(StyleClasses styleClasses) {
187        for (String clazz : (Iterable<String>) styleClasses.classes) {
188          classes.add(clazz);
189        }
190      }
191    
192      public void removeClass(String clazz) {
193        classes.remove(clazz);
194      }
195    
196      public void removeTobagoClasses(String rendererName) {
197        for (Iterator i = classes.iterator(); i.hasNext();) {
198          String clazz = (String) i.next();
199          if (clazz.startsWith(PREFIX + rendererName)) {
200            i.remove();
201          }
202        }
203      }
204    
205      public void updateClassAttributeAndMarkup(UIComponent component, String rendererName) {
206        updateClassAttribute(component, rendererName);
207        addMarkupClass(component, rendererName);
208      }
209    
210      public void updateClassAttribute(UIComponent component, String rendererName) {
211        // first remove old tobago-<rendererName>-<type> classes from class-attribute
212        removeTobagoClasses(rendererName);
213    
214        addAspectClass(rendererName, Aspect.DEFAULT);
215        if (ComponentUtil.getBooleanAttribute(component, ATTR_DISABLED)) {
216          addAspectClass(rendererName, Aspect.DISABLED);
217        }
218        if (ComponentUtil.getBooleanAttribute(component, ATTR_READONLY)) {
219          addAspectClass(rendererName, Aspect.READONLY);
220        }
221        if (ComponentUtil.getBooleanAttribute(component, ATTR_INLINE)) {
222          addAspectClass(rendererName, Aspect.INLINE);
223        }
224        FacesMessage.Severity severity = ComponentUtil.getMaximumSeverity(component);
225        if (severity != null) {
226          if (severity.equals(FacesMessage.SEVERITY_FATAL)) {
227            addMarkupClass(rendererName, "fatal");
228            addAspectClass(rendererName, Aspect.ERROR);
229          } else if (severity.equals(FacesMessage.SEVERITY_ERROR)) {
230            addMarkupClass(rendererName, "error");
231            addAspectClass(rendererName, Aspect.ERROR);
232          } else if (severity.equals(FacesMessage.SEVERITY_WARN)) {
233            addMarkupClass(rendererName, "warn");
234          } else if (severity.equals(FacesMessage.SEVERITY_INFO)) {
235            addMarkupClass(rendererName, "info");
236          } else {
237            assert false : "Ordinal constants may be wrong";
238          }
239        }
240        if (component instanceof UIInput) {
241          UIInput input = (UIInput) component;
242          if (input.isRequired()) {
243            addAspectClass(rendererName, Aspect.REQUIRED);
244          }
245        }
246      }
247    
248      public boolean isEmpty() {
249        return classes.isEmpty();
250      }
251    
252      @Override
253      public String toString() {
254        if (classes.isEmpty()) {
255          return null;
256        }
257        StringBuilder buffer = new StringBuilder(32);
258        for (Iterator i = classes.iterator(); i.hasNext();) {
259          String clazz = (String) i.next();
260          buffer.append(clazz);
261          if (i.hasNext()) {
262            buffer.append(' ');
263          }
264        }
265        return buffer.toString();
266      }
267    
268      public enum Aspect {
269    
270        DEFAULT,
271        DISABLED,
272        READONLY,
273        INLINE,
274        ERROR,
275        REQUIRED;
276    
277        private String aspect;
278    
279        Aspect() {
280          aspect = '-' + name().toLowerCase(Locale.ENGLISH);
281        }
282    
283        @Override
284        public String toString() {
285          return aspect;
286        }
287      }
288    }