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();
100        builder.append(PREFIX);
101        builder.append(renderer);
102        builder.append(SEPERATOR);
103        builder.append(sub);
104        return builder.toString();
105      }
106    
107      public void addMarkupClass(String renderer, String markup) {
108        addMarkupClass(renderer, null, markup);
109      }
110    
111      public void removeMarkupClass(String renderer, String markup) {
112        removeMarkupClass(renderer, null, markup);
113      }
114    
115      public void addMarkupClass(String renderer, String sub, String markup) {
116        classes.add(nameOfMarkupClass(renderer, sub, markup));
117      }
118    
119      public void removeMarkupClass(String renderer, String sub, String markup) {
120        classes.remove(nameOfMarkupClass(renderer, sub, markup));
121      }
122    
123      private String nameOfMarkupClass(String renderer, String sub, String markup) {
124        StringBuilder builder = new StringBuilder();
125        builder.append(PREFIX);
126        builder.append(renderer);
127        if (sub != null) {
128          builder.append(SEPERATOR);
129          builder.append(sub);
130        }
131        builder.append(MARKUP);
132        builder.append(markup);
133        return builder.toString();
134      }
135    
136      public void addMarkupClass(UIComponent component, String rendererName) {
137        addMarkupClass(component, rendererName, null);
138      }
139    
140      public void addMarkupClass(UIComponent component, String rendererName, String sub) {
141        if (component instanceof SupportsMarkup) {
142          String[] markups = ((SupportsMarkup) component).getMarkup();
143          for (String markup: markups) {
144            if (!StringUtils.isBlank(markup)) {
145              Theme theme = ClientProperties.getInstance(FacesContext.getCurrentInstance().getViewRoot()).getTheme();
146              if (theme.getRenderersConfig().isMarkupSupported(rendererName, markup)) {
147                addMarkupClass(rendererName, sub, markup);
148              } else if (!"none".equals(markup)) {
149                LOG.warn("Unknown markup='" + markup + "'");
150              }
151            }
152          }
153        }
154      }
155    
156      public void addAspectClass(String renderer, Aspect aspect) {
157        classes.add(nameOfAspectClass(renderer, aspect));
158      }
159    
160      public void removeAspectClass(String renderer, Aspect aspect) {
161        classes.remove(nameOfAspectClass(renderer, aspect));
162      }
163    
164      private String nameOfAspectClass(String renderer, Aspect aspect) {
165        StringBuilder builder = new StringBuilder();
166        builder.append(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();
183        builder.append(PREFIX);
184        builder.append(renderer);
185        builder.append(SEPERATOR);
186        builder.append(sub);
187        builder.append(aspect);
188        return builder.toString();
189      }
190    
191      public void addClasses(StyleClasses styleClasses) {
192        for (String clazz : (Iterable<String>) styleClasses.classes) {
193          classes.add(clazz);
194        }
195      }
196    
197      public void removeClass(String clazz) {
198        classes.remove(clazz);
199      }
200    
201      public void removeTobagoClasses(String rendererName) {
202        for (Iterator i = classes.iterator(); i.hasNext();) {
203          String clazz = (String) i.next();
204          if (clazz.startsWith(PREFIX + rendererName)) {
205            i.remove();
206          }
207        }
208      }
209    
210      public void updateClassAttributeAndMarkup(UIComponent component, String rendererName) {
211        updateClassAttribute(component, rendererName);
212        addMarkupClass(component, rendererName);
213      }
214    
215      public void updateClassAttribute(UIComponent component, String rendererName) {
216        // first remove old tobago-<rendererName>-<type> classes from class-attribute
217        removeTobagoClasses(rendererName);
218    
219        addAspectClass(rendererName, Aspect.DEFAULT);
220        if (ComponentUtil.getBooleanAttribute(component, ATTR_DISABLED)) {
221          addAspectClass(rendererName, Aspect.DISABLED);
222        }
223        if (ComponentUtil.getBooleanAttribute(component, ATTR_READONLY)) {
224          addAspectClass(rendererName, Aspect.READONLY);
225        }
226        if (ComponentUtil.getBooleanAttribute(component, ATTR_INLINE)) {
227          addAspectClass(rendererName, Aspect.INLINE);
228        }
229        if (component instanceof UIInput) {
230          UIInput input = (UIInput) component;
231          if (ComponentUtil.isError(input)) {
232            addAspectClass(rendererName, Aspect.ERROR);
233          }
234          if (input.isRequired()) {
235            addAspectClass(rendererName, Aspect.REQUIRED);
236          }
237        }
238      }
239    
240      @Override
241      public String toString() {
242        StringBuffer buffer = new StringBuffer();
243        for (Iterator i = classes.iterator(); i.hasNext();) {
244          String clazz = (String) i.next();
245          buffer.append(clazz);
246          if (i.hasNext()) {
247            buffer.append(' ');
248          }
249        }
250        return buffer.toString();
251      }
252    
253      public enum Aspect {
254    
255        DEFAULT,
256        DISABLED,
257        READONLY,
258        INLINE,
259        ERROR,
260        REQUIRED;
261    
262        private String aspect;
263    
264        Aspect() {
265          aspect = '-' + name().toLowerCase(Locale.ENGLISH);
266        }
267    
268        @Override
269        public String toString() {
270          return aspect;
271        }
272      }
273    }