001    package org.apache.myfaces.tobago.renderkit.html;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one or more
005     * contributor license agreements.  See the NOTICE file distributed with
006     * this work for additional information regarding copyright ownership.
007     * The ASF licenses this file to You under the Apache License, Version 2.0
008     * (the "License"); you may not use this file except in compliance with
009     * the License.  You may obtain a copy of the License at
010     *
011     *      http://www.apache.org/licenses/LICENSE-2.0
012     *
013     * Unless required by applicable law or agreed to in writing, software
014     * distributed under the License is distributed on an "AS IS" BASIS,
015     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016     * See the License for the specific language governing permissions and
017     * limitations under the License.
018     */
019    
020    import org.apache.commons.collections.set.ListOrderedSet;
021    import org.apache.commons.logging.Log;
022    import org.apache.commons.logging.LogFactory;
023    import org.apache.commons.lang.StringUtils;
024    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_DISABLED;
025    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_INLINE;
026    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_READONLY;
027    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_STYLE_CLASS;
028    import org.apache.myfaces.tobago.util.ComponentUtil;
029    import org.apache.myfaces.tobago.component.SupportsMarkup;
030    import org.apache.myfaces.tobago.context.ClientProperties;
031    import org.apache.myfaces.tobago.context.Theme;
032    
033    import javax.faces.component.UIComponent;
034    import javax.faces.component.UIInput;
035    import javax.faces.context.FacesContext;
036    import java.io.Serializable;
037    import java.util.Iterator;
038    import java.util.Locale;
039    import java.util.Map;
040    
041    /*
042     * Date: 2007-05-01
043     */
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      public boolean isEmpty() {
099        return classes.isEmpty();
100      }
101    
102      private String nameOfClass(String renderer, String sub) {
103        StringBuilder builder = new StringBuilder(PREFIX);
104        builder.append(renderer);
105        builder.append(SEPERATOR);
106        builder.append(sub);
107        return builder.toString();
108      }
109    
110      public void addMarkupClass(String renderer, String markup) {
111        addMarkupClass(renderer, null, markup);
112      }
113    
114      public void removeMarkupClass(String renderer, String markup) {
115        removeMarkupClass(renderer, null, markup);
116      }
117    
118      public void addMarkupClass(String renderer, String sub, String markup) {
119        classes.add(nameOfMarkupClass(renderer, sub, markup));
120      }
121    
122      public void removeMarkupClass(String renderer, String sub, String markup) {
123        classes.remove(nameOfMarkupClass(renderer, sub, markup));
124      }
125    
126      private String nameOfMarkupClass(String renderer, String sub, String markup) {
127        StringBuilder builder = new StringBuilder(PREFIX);
128        builder.append(renderer);
129        if (sub != null) {
130          builder.append(SEPERATOR);
131          builder.append(sub);
132        }
133        builder.append(MARKUP);
134        builder.append(markup);
135        return builder.toString();
136      }
137    
138      public void addMarkupClass(UIComponent component, String rendererName) {
139        if (component instanceof SupportsMarkup) {
140          addMarkupClass((SupportsMarkup) component, rendererName, null);
141        }
142      }
143    
144      public void addMarkupClass(SupportsMarkup supportsMarkup, String rendererName, String sub) {
145        for (String markup : supportsMarkup.getMarkup()) {
146          if (!StringUtils.isBlank(markup)) {
147            Theme theme = ClientProperties.getInstance(FacesContext.getCurrentInstance().getViewRoot()).getTheme();
148            if (theme.getRenderersConfig().isMarkupSupported(rendererName, markup)) {
149              addMarkupClass(rendererName, sub, markup);
150            } else if (!"none".equals(markup)) {
151              LOG.warn("Unknown markup='" + markup + "'");
152            }
153          }
154        }
155      }
156    
157      public void addAspectClass(String renderer, Aspect aspect) {
158        classes.add(nameOfAspectClass(renderer, aspect));
159      }
160    
161      public void removeAspectClass(String renderer, Aspect aspect) {
162        classes.remove(nameOfAspectClass(renderer, aspect));
163      }
164    
165      private String nameOfAspectClass(String renderer, Aspect aspect) {
166        StringBuilder builder = new StringBuilder(PREFIX);
167        builder.append(renderer);
168        builder.append(aspect);
169        return builder.toString();
170      }
171    
172    
173      public void addAspectClass(String renderer, String sub, Aspect aspect) {
174        classes.add(nameOfAspectClass(renderer, sub, aspect));
175      }
176    
177      public void removeAspectClass(String renderer, String sub, Aspect aspect) {
178        classes.remove(nameOfAspectClass(renderer, sub, aspect));
179      }
180    
181      private String nameOfAspectClass(String renderer, String sub, Aspect aspect) {
182        StringBuilder builder = new StringBuilder(PREFIX);
183        builder.append(renderer);
184        builder.append(SEPERATOR);
185        builder.append(sub);
186        builder.append(aspect);
187        return builder.toString();
188      }
189    
190      public void addClasses(StyleClasses styleClasses) {
191        for (String clazz : (Iterable<String>) styleClasses.classes) {
192          classes.add(clazz);
193        }
194      }
195    
196      public void removeClass(String clazz) {
197        classes.remove(clazz);
198      }
199    
200      public void removeTobagoClasses(String rendererName) {
201        for (Iterator i = classes.iterator(); i.hasNext();) {
202          String clazz = (String) i.next();
203          if (clazz.startsWith(PREFIX + rendererName)) {
204            i.remove();
205          }
206        }
207      }
208    
209      public void updateClassAttributeAndMarkup(UIComponent component, String rendererName) {
210        updateClassAttribute(component, rendererName);
211        addMarkupClass(component, rendererName);
212      }
213    
214      public void updateClassAttribute(UIComponent component, String rendererName) {
215        // first remove old tobago-<rendererName>-<type> classes from class-attribute
216        removeTobagoClasses(rendererName);
217    
218        addAspectClass(rendererName, Aspect.DEFAULT);
219        if (ComponentUtil.getBooleanAttribute(component, ATTR_DISABLED)) {
220          addAspectClass(rendererName, Aspect.DISABLED);
221        }
222        if (ComponentUtil.getBooleanAttribute(component, ATTR_READONLY)) {
223          addAspectClass(rendererName, Aspect.READONLY);
224        }
225        if (ComponentUtil.getBooleanAttribute(component, ATTR_INLINE)) {
226          addAspectClass(rendererName, Aspect.INLINE);
227        }
228        if (component instanceof UIInput) {
229          UIInput input = (UIInput) component;
230          if (ComponentUtil.isError(input)) {
231            addAspectClass(rendererName, Aspect.ERROR);
232          }
233          if (input.isRequired()) {
234            addAspectClass(rendererName, Aspect.REQUIRED);
235          }
236        }
237      }
238    
239      @Override
240      public String toString() {
241        if (classes.isEmpty()) {
242          return null;
243        }
244        StringBuilder buffer = new StringBuilder(16 * classes.size());
245        for (Iterator i = classes.iterator(); i.hasNext();) {
246          String clazz = (String) i.next();
247          buffer.append(clazz);
248          if (i.hasNext()) {
249            buffer.append(' ');
250          }
251        }
252        return buffer.toString();
253      }
254    
255      public enum Aspect {
256    
257        DEFAULT,
258        DISABLED,
259        READONLY,
260        INLINE,
261        ERROR,
262        REQUIRED;
263    
264        private String aspect;
265    
266        Aspect() {
267          aspect = '-' + name().toLowerCase(Locale.ENGLISH);
268        }
269    
270        @Override
271        public String toString() {
272          return aspect;
273        }
274      }
275    }