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.component.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      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("Unknown markup='" + markup + "'");
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        if (component instanceof UIInput) {
225          UIInput input = (UIInput) component;
226          if (ComponentUtil.isError(input)) {
227            addAspectClass(rendererName, Aspect.ERROR);
228          }
229          if (input.isRequired()) {
230            addAspectClass(rendererName, Aspect.REQUIRED);
231          }
232        }
233      }
234    
235      public boolean isEmpty() {
236        return classes.isEmpty();  
237      }
238    
239      @Override
240      public String toString() {
241        if (classes.isEmpty()) {
242          return null;
243        }
244        StringBuilder buffer = new StringBuilder(32);
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    }